Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelv...
Linus Torvalds [Wed, 25 May 2011 23:52:50 +0000 (16:52 -0700)]
* 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging:
  hwmon: New driver for the SMSC EMC6W201
  hwmon: (abituguru) Depend on DMI
  hwmon: (it87) Use request_muxed_region
  hwmon: (sch5627) Trigger Vbat measurements
  hwmon: (sch5627) Add sch5627_send_cmd function
  i8k: Integrate with the hwmon subsystem
  hwmon: (max6650) Properly support the MAX6650
  hwmon: (max6650) Drop device detection
  Move ACPI power meter driver to hwmon
  hwmon: (f71882fg) Add support for F71808A
  hwmon: (f71882fg) Split has_beep in fan_has_beep and temp_has_beep
  hwmon: (asc7621) Drop duplicate dependency
  hwmon: (jc42) Change detection class
  hwmon: Add driver for AMD family 15h processor power information
  hwmon: (k10temp) Add support for Fam15h (Bulldozer)
  hwmon: Use helper functions to set and get driver data
  i8k: Avoid lahf in 64-bit code

29 files changed:
Documentation/hwmon/emc6w201 [new file with mode: 0644]
Documentation/hwmon/f71882fg
Documentation/hwmon/fam15h_power [new file with mode: 0644]
Documentation/hwmon/k10temp
Documentation/hwmon/max6650
MAINTAINERS
arch/x86/Kconfig
drivers/acpi/Kconfig
drivers/acpi/Makefile
drivers/char/i8k.c
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/abituguru.c
drivers/hwmon/abituguru3.c
drivers/hwmon/acpi_power_meter.c [moved from drivers/acpi/power_meter.c with 100% similarity]
drivers/hwmon/adcxx.c
drivers/hwmon/emc6w201.c [new file with mode: 0644]
drivers/hwmon/f71882fg.c
drivers/hwmon/fam15h_power.c [new file with mode: 0644]
drivers/hwmon/ibmaem.c
drivers/hwmon/it87.c
drivers/hwmon/jc42.c
drivers/hwmon/k10temp.c
drivers/hwmon/k8temp.c
drivers/hwmon/lm70.c
drivers/hwmon/max6650.c
drivers/hwmon/sch5627.c
drivers/hwmon/ultra45_env.c
include/linux/i2c.h

diff --git a/Documentation/hwmon/emc6w201 b/Documentation/hwmon/emc6w201
new file mode 100644 (file)
index 0000000..32f355a
--- /dev/null
@@ -0,0 +1,42 @@
+Kernel driver emc6w201
+======================
+
+Supported chips:
+  * SMSC EMC6W201
+    Prefix: 'emc6w201'
+    Addresses scanned: I2C 0x2c, 0x2d, 0x2e
+    Datasheet: Not public
+
+Author: Jean Delvare <khali@linux-fr.org>
+
+
+Description
+-----------
+
+From the datasheet:
+
+"The EMC6W201 is an environmental monitoring device with automatic fan
+control capability and enhanced system acoustics for noise suppression.
+This ACPI compliant device provides hardware monitoring for up to six
+voltages (including its own VCC) and five external thermal sensors,
+measures the speed of up to five fans, and controls the speed of
+multiple DC fans using three Pulse Width Modulator (PWM) outputs. Note
+that it is possible to control more than three fans by connecting two
+fans to one PWM output. The EMC6W201 will be available in a 36-pin
+QFN package."
+
+The device is functionally close to the EMC6D100 series, but is
+register-incompatible.
+
+The driver currently only supports the monitoring of the voltages,
+temperatures and fan speeds. Limits can be changed. Alarms are not
+supported, and neither is fan speed control.
+
+
+Known Systems With EMC6W201
+---------------------------
+
+The EMC6W201 is a rare device, only found on a few systems, made in
+2005 and 2006. Known systems with this device:
+* Dell Precision 670 workstation
+* Gigabyte 2CEWH mainboard
index df02245..84d2623 100644 (file)
@@ -6,6 +6,10 @@ Supported chips:
     Prefix: 'f71808e'
     Addresses scanned: none, address read from Super I/O config space
     Datasheet: Not public
+  * Fintek F71808A
+    Prefix: 'f71808a'
+    Addresses scanned: none, address read from Super I/O config space
+    Datasheet: Not public
   * Fintek F71858FG
     Prefix: 'f71858fg'
     Addresses scanned: none, address read from Super I/O config space
diff --git a/Documentation/hwmon/fam15h_power b/Documentation/hwmon/fam15h_power
new file mode 100644 (file)
index 0000000..a92918e
--- /dev/null
@@ -0,0 +1,37 @@
+Kernel driver fam15h_power
+==========================
+
+Supported chips:
+* AMD Family 15h Processors
+
+  Prefix: 'fam15h_power'
+  Addresses scanned: PCI space
+  Datasheets:
+  BIOS and Kernel Developer's Guide (BKDG) For AMD Family 15h Processors
+    (not yet published)
+
+Author: Andreas Herrmann <andreas.herrmann3@amd.com>
+
+Description
+-----------
+
+This driver permits reading of registers providing power information
+of AMD Family 15h processors.
+
+For AMD Family 15h processors the following power values can be
+calculated using different processor northbridge function registers:
+
+* BasePwrWatts: Specifies in watts the maximum amount of power
+  consumed by the processor for NB and logic external to the core.
+* ProcessorPwrWatts: Specifies in watts the maximum amount of power
+  the processor can support.
+* CurrPwrWatts: Specifies in watts the current amount of power being
+  consumed by the processor.
+
+This driver provides ProcessorPwrWatts and CurrPwrWatts:
+* power1_crit (ProcessorPwrWatts)
+* power1_input (CurrPwrWatts)
+
+On multi-node processors the calculated value is for the entire
+package and not for a single node. Thus the driver creates sysfs
+attributes only for internal node0 of a multi-node processor.
index d2b56a4..0393c89 100644 (file)
@@ -11,6 +11,7 @@ Supported chips:
   Socket S1G2: Athlon (X2), Sempron (X2), Turion X2 (Ultra)
 * AMD Family 12h processors: "Llano"
 * AMD Family 14h processors: "Brazos" (C/E/G-Series)
+* AMD Family 15h processors: "Bulldozer"
 
   Prefix: 'k10temp'
   Addresses scanned: PCI space
@@ -40,7 +41,7 @@ Description
 -----------
 
 This driver permits reading of the internal temperature sensor of AMD
-Family 10h/11h/12h/14h processors.
+Family 10h/11h/12h/14h/15h processors.
 
 All these processors have a sensor, but on those for Socket F or AM2+,
 the sensor may return inconsistent values (erratum 319).  The driver
index c565650..58d9644 100644 (file)
@@ -2,9 +2,13 @@ Kernel driver max6650
 =====================
 
 Supported chips:
-  * Maxim 6650 / 6651
+  * Maxim MAX6650
     Prefix: 'max6650'
-    Addresses scanned: I2C 0x1b, 0x1f, 0x48, 0x4b
+    Addresses scanned: none
+    Datasheet: http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf
+  * Maxim MAX6651
+    Prefix: 'max6651'
+    Addresses scanned: none
     Datasheet: http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf
 
 Authors:
@@ -15,10 +19,10 @@ Authors:
 Description
 -----------
 
-This driver implements support for the Maxim 6650/6651
+This driver implements support for the Maxim MAX6650 and MAX6651.
 
-The 2 devices are very similar, but the Maxim 6550 has a reduced feature
-set, e.g. only one fan-input, instead of 4 for the 6651.
+The 2 devices are very similar, but the MAX6550 has a reduced feature
+set, e.g. only one fan-input, instead of 4 for the MAX6651.
 
 The driver is not able to distinguish between the 2 devices.
 
@@ -36,6 +40,13 @@ fan1_div     rw      sets the speed range the inputs can handle. Legal
                        values are 1, 2, 4, and 8. Use lower values for
                        faster fans.
 
+Usage notes
+-----------
+
+This driver does not auto-detect devices. You will have to instantiate the
+devices explicitly. Please see Documentation/i2c/instantiating-devices for
+details.
+
 Module parameters
 -----------------
 
index f5b6295..a26c9ee 100644 (file)
@@ -483,6 +483,13 @@ F: drivers/tty/serial/altera_jtaguart.c
 F:     include/linux/altera_uart.h
 F:     include/linux/altera_jtaguart.h
 
+AMD FAM15H PROCESSOR POWER MONITORING DRIVER
+M:     Andreas Herrmann <andreas.herrmann3@amd.com>
+L:     lm-sensors@lm-sensors.org
+S:     Maintained
+F:     Documentation/hwmon/fam15h_power
+F:     drivers/hwmon/fam15h_power.c
+
 AMD GEODE CS5536 USB DEVICE CONTROLLER DRIVER
 M:     Thomas Dahlmann <dahlmann.thomas@arcor.de>
 L:     linux-geode@lists.infradead.org (moderated for non-subscribers)
index fa2cc8c..483775f 100644 (file)
@@ -915,6 +915,7 @@ config TOSHIBA
 
 config I8K
        tristate "Dell laptop support"
+       select HWMON
        ---help---
          This adds a driver to safely access the System Management Mode
          of the CPU on the Dell Inspiron 8000. The System Management Mode
index 3a17ca5..bc2218d 100644 (file)
@@ -73,17 +73,6 @@ config ACPI_PROCFS_POWER
 
          Say N to delete power /proc/acpi/ directories that have moved to /sys/
 
-config ACPI_POWER_METER
-       tristate "ACPI 4.0 power meter"
-       depends on HWMON
-       help
-         This driver exposes ACPI 4.0 power meters as hardware monitoring
-         devices.  Say Y (or M) if you have a computer with ACPI 4.0 firmware
-         and a power meter.
-
-         To compile this driver as a module, choose M here:
-         the module will be called power-meter.
-
 config ACPI_EC_DEBUGFS
        tristate "EC read/write access through /sys/kernel/debug/ec"
        default n
index d113fa5..b66fbb2 100644 (file)
@@ -59,7 +59,6 @@ obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o
 obj-$(CONFIG_ACPI_BATTERY)     += battery.o
 obj-$(CONFIG_ACPI_SBS)         += sbshc.o
 obj-$(CONFIG_ACPI_SBS)         += sbs.o
-obj-$(CONFIG_ACPI_POWER_METER) += power_meter.o
 obj-$(CONFIG_ACPI_HED)         += hed.o
 obj-$(CONFIG_ACPI_EC_DEBUGFS)  += ec_sys.o
 
index d72433f..6e40072 100644 (file)
@@ -5,6 +5,9 @@
  *
  * Copyright (C) 2001  Massimo Dal Zotto <dz@debian.org>
  *
+ * Hwmon integration:
+ * Copyright (C) 2011  Jean Delvare <khali@linux-fr.org>
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2, or (at your option) any
@@ -24,6 +27,8 @@
 #include <linux/dmi.h>
 #include <linux/capability.h>
 #include <linux/mutex.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 
@@ -58,6 +63,7 @@
 
 static DEFINE_MUTEX(i8k_mutex);
 static char bios_version[4];
+static struct device *i8k_hwmon_dev;
 
 MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
 MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops");
@@ -139,8 +145,8 @@ static int i8k_smm(struct smm_regs *regs)
                "movl %%edi,20(%%rax)\n\t"
                "popq %%rdx\n\t"
                "movl %%edx,0(%%rax)\n\t"
-               "lahf\n\t"
-               "shrl $8,%%eax\n\t"
+               "pushfq\n\t"
+               "popq %%rax\n\t"
                "andl $1,%%eax\n"
                :"=a"(rc)
                :    "a"(regs)
@@ -455,6 +461,152 @@ static int i8k_open_fs(struct inode *inode, struct file *file)
        return single_open(file, i8k_proc_show, NULL);
 }
 
+
+/*
+ * Hwmon interface
+ */
+
+static ssize_t i8k_hwmon_show_temp(struct device *dev,
+                                  struct device_attribute *devattr,
+                                  char *buf)
+{
+       int cpu_temp;
+
+       cpu_temp = i8k_get_temp(0);
+       if (cpu_temp < 0)
+               return cpu_temp;
+       return sprintf(buf, "%d\n", cpu_temp * 1000);
+}
+
+static ssize_t i8k_hwmon_show_fan(struct device *dev,
+                                 struct device_attribute *devattr,
+                                 char *buf)
+{
+       int index = to_sensor_dev_attr(devattr)->index;
+       int fan_speed;
+
+       fan_speed = i8k_get_fan_speed(index);
+       if (fan_speed < 0)
+               return fan_speed;
+       return sprintf(buf, "%d\n", fan_speed);
+}
+
+static ssize_t i8k_hwmon_show_label(struct device *dev,
+                                   struct device_attribute *devattr,
+                                   char *buf)
+{
+       static const char *labels[4] = {
+               "i8k",
+               "CPU",
+               "Left Fan",
+               "Right Fan",
+       };
+       int index = to_sensor_dev_attr(devattr)->index;
+
+       return sprintf(buf, "%s\n", labels[index]);
+}
+
+static DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL);
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
+                         I8K_FAN_LEFT);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
+                         I8K_FAN_RIGHT);
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, i8k_hwmon_show_label, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 3);
+
+static void i8k_hwmon_remove_files(struct device *dev)
+{
+       device_remove_file(dev, &dev_attr_temp1_input);
+       device_remove_file(dev, &sensor_dev_attr_fan1_input.dev_attr);
+       device_remove_file(dev, &sensor_dev_attr_fan2_input.dev_attr);
+       device_remove_file(dev, &sensor_dev_attr_temp1_label.dev_attr);
+       device_remove_file(dev, &sensor_dev_attr_fan1_label.dev_attr);
+       device_remove_file(dev, &sensor_dev_attr_fan2_label.dev_attr);
+       device_remove_file(dev, &sensor_dev_attr_name.dev_attr);
+}
+
+static int __init i8k_init_hwmon(void)
+{
+       int err;
+
+       i8k_hwmon_dev = hwmon_device_register(NULL);
+       if (IS_ERR(i8k_hwmon_dev)) {
+               err = PTR_ERR(i8k_hwmon_dev);
+               i8k_hwmon_dev = NULL;
+               printk(KERN_ERR "i8k: hwmon registration failed (%d)\n", err);
+               return err;
+       }
+
+       /* Required name attribute */
+       err = device_create_file(i8k_hwmon_dev,
+                                &sensor_dev_attr_name.dev_attr);
+       if (err)
+               goto exit_unregister;
+
+       /* CPU temperature attributes, if temperature reading is OK */
+       err = i8k_get_temp(0);
+       if (err < 0) {
+               dev_dbg(i8k_hwmon_dev,
+                       "Not creating temperature attributes (%d)\n", err);
+       } else {
+               err = device_create_file(i8k_hwmon_dev, &dev_attr_temp1_input);
+               if (err)
+                       goto exit_remove_files;
+               err = device_create_file(i8k_hwmon_dev,
+                                        &sensor_dev_attr_temp1_label.dev_attr);
+               if (err)
+                       goto exit_remove_files;
+       }
+
+       /* Left fan attributes, if left fan is present */
+       err = i8k_get_fan_status(I8K_FAN_LEFT);
+       if (err < 0) {
+               dev_dbg(i8k_hwmon_dev,
+                       "Not creating %s fan attributes (%d)\n", "left", err);
+       } else {
+               err = device_create_file(i8k_hwmon_dev,
+                                        &sensor_dev_attr_fan1_input.dev_attr);
+               if (err)
+                       goto exit_remove_files;
+               err = device_create_file(i8k_hwmon_dev,
+                                        &sensor_dev_attr_fan1_label.dev_attr);
+               if (err)
+                       goto exit_remove_files;
+       }
+
+       /* Right fan attributes, if right fan is present */
+       err = i8k_get_fan_status(I8K_FAN_RIGHT);
+       if (err < 0) {
+               dev_dbg(i8k_hwmon_dev,
+                       "Not creating %s fan attributes (%d)\n", "right", err);
+       } else {
+               err = device_create_file(i8k_hwmon_dev,
+                                        &sensor_dev_attr_fan2_input.dev_attr);
+               if (err)
+                       goto exit_remove_files;
+               err = device_create_file(i8k_hwmon_dev,
+                                        &sensor_dev_attr_fan2_label.dev_attr);
+               if (err)
+                       goto exit_remove_files;
+       }
+
+       return 0;
+
+ exit_remove_files:
+       i8k_hwmon_remove_files(i8k_hwmon_dev);
+ exit_unregister:
+       hwmon_device_unregister(i8k_hwmon_dev);
+       return err;
+}
+
+static void __exit i8k_exit_hwmon(void)
+{
+       i8k_hwmon_remove_files(i8k_hwmon_dev);
+       hwmon_device_unregister(i8k_hwmon_dev);
+}
+
 static struct dmi_system_id __initdata i8k_dmi_table[] = {
        {
                .ident = "Dell Inspiron",
@@ -580,6 +732,7 @@ static int __init i8k_probe(void)
 static int __init i8k_init(void)
 {
        struct proc_dir_entry *proc_i8k;
+       int err;
 
        /* Are we running on an supported laptop? */
        if (i8k_probe())
@@ -590,15 +743,24 @@ static int __init i8k_init(void)
        if (!proc_i8k)
                return -ENOENT;
 
+       err = i8k_init_hwmon();
+       if (err)
+               goto exit_remove_proc;
+
        printk(KERN_INFO
               "Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)\n",
               I8K_VERSION);
 
        return 0;
+
+ exit_remove_proc:
+       remove_proc_entry("i8k", NULL);
+       return err;
 }
 
 static void __exit i8k_exit(void)
 {
+       i8k_exit_hwmon();
        remove_proc_entry("i8k", NULL);
 }
 
index 43221be..16db83c 100644 (file)
@@ -41,7 +41,7 @@ comment "Native drivers"
 
 config SENSORS_ABITUGURU
        tristate "Abit uGuru (rev 1 & 2)"
-       depends on X86 && EXPERIMENTAL
+       depends on X86 && DMI && EXPERIMENTAL
        help
          If you say yes here you get support for the sensor part of the first
          and second revision of the Abit uGuru chip. The voltage and frequency
@@ -56,7 +56,7 @@ config SENSORS_ABITUGURU
 
 config SENSORS_ABITUGURU3
        tristate "Abit uGuru (rev 3)"
-       depends on X86 && EXPERIMENTAL
+       depends on X86 && DMI && EXPERIMENTAL
        help
          If you say yes here you get support for the sensor part of the
          third revision of the Abit uGuru chip. Only reading the sensors
@@ -213,7 +213,7 @@ config SENSORS_ADT7475
 
 config SENSORS_ASC7621
        tristate "Andigilog aSC7621"
-       depends on HWMON && I2C
+       depends on I2C
        help
          If you say yes here you get support for the aSC7621
          family of SMBus sensors chip found on most Intel X38, X48, X58,
@@ -237,17 +237,27 @@ config SENSORS_K8TEMP
          will be called k8temp.
 
 config SENSORS_K10TEMP
-       tristate "AMD Family 10h/11h/12h/14h temperature sensor"
+       tristate "AMD Family 10h+ temperature sensor"
        depends on X86 && PCI
        help
          If you say yes here you get support for the temperature
          sensor(s) inside your CPU. Supported are later revisions of
          the AMD Family 10h and all revisions of the AMD Family 11h,
-         12h (Llano), and 14h (Brazos) microarchitectures.
+         12h (Llano), 14h (Brazos) and 15h (Bulldozer) microarchitectures.
 
          This driver can also be built as a module.  If so, the module
          will be called k10temp.
 
+config SENSORS_FAM15H_POWER
+       tristate "AMD Family 15h processor power"
+       depends on X86 && PCI
+       help
+         If you say yes here you get support for processor power
+         information of your AMD family 15h CPU.
+
+         This driver can also be built as a module.  If so, the module
+         will be called fam15h_power.
+
 config SENSORS_ASB100
        tristate "Asus ASB100 Bach"
        depends on X86 && I2C && EXPERIMENTAL
@@ -319,7 +329,7 @@ config SENSORS_F71882FG
          If you say yes here you get support for hardware monitoring
          features of many Fintek Super-I/O (LPC) chips. The currently
          supported chips are:
-           F71808E
+           F71808E/A
            F71858FG
            F71862FG
            F71863FG
@@ -978,6 +988,16 @@ config SENSORS_EMC2103
          This driver can also be built as a module.  If so, the module
          will be called emc2103.
 
+config SENSORS_EMC6W201
+       tristate "SMSC EMC6W201"
+       depends on I2C
+       help
+         If you say yes here you get support for the SMSC EMC6W201
+         hardware monitoring chip.
+
+         This driver can also be built as a module.  If so, the module
+         will be called emc6w201.
+
 config SENSORS_SMSC47M1
        tristate "SMSC LPC47M10x and compatibles"
        help
@@ -1341,6 +1361,16 @@ if ACPI
 
 comment "ACPI drivers"
 
+config SENSORS_ACPI_POWER
+       tristate "ACPI 4.0 power meter"
+       help
+         This driver exposes ACPI 4.0 power meters as hardware monitoring
+         devices.  Say Y (or M) if you have a computer with ACPI 4.0 firmware
+         and a power meter.
+
+         To compile this driver as a module, choose M here:
+         the module will be called acpi_power_meter.
+
 config SENSORS_ATK0110
        tristate "ASUS ATK0110"
        depends on X86 && EXPERIMENTAL
index 28e8d52..28061cf 100644 (file)
@@ -6,6 +6,7 @@ obj-$(CONFIG_HWMON)             += hwmon.o
 obj-$(CONFIG_HWMON_VID)                += hwmon-vid.o
 
 # APCI drivers
+obj-$(CONFIG_SENSORS_ACPI_POWER) += acpi_power_meter.o
 obj-$(CONFIG_SENSORS_ATK0110)  += asus_atk0110.o
 
 # Native drivers
@@ -45,9 +46,11 @@ obj-$(CONFIG_SENSORS_DS620)  += ds620.o
 obj-$(CONFIG_SENSORS_DS1621)   += ds1621.o
 obj-$(CONFIG_SENSORS_EMC1403)  += emc1403.o
 obj-$(CONFIG_SENSORS_EMC2103)  += emc2103.o
+obj-$(CONFIG_SENSORS_EMC6W201) += emc6w201.o
 obj-$(CONFIG_SENSORS_F71805F)  += f71805f.o
 obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o
 obj-$(CONFIG_SENSORS_F75375S)  += f75375s.o
+obj-$(CONFIG_SENSORS_FAM15H_POWER) += fam15h_power.o
 obj-$(CONFIG_SENSORS_FSCHMD)   += fschmd.o
 obj-$(CONFIG_SENSORS_G760A)    += g760a.o
 obj-$(CONFIG_SENSORS_GL518SM)  += gl518sm.o
index e7d4c46..65a35cf 100644 (file)
@@ -1448,15 +1448,12 @@ static int __init abituguru_init(void)
 {
        int address, err;
        struct resource res = { .flags = IORESOURCE_IO };
-
-#ifdef CONFIG_DMI
        const char *board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
 
        /* safety check, refuse to load on non Abit motherboards */
        if (!force && (!board_vendor ||
                        strcmp(board_vendor, "http://www.abit.com.tw/")))
                return -ENODEV;
-#endif
 
        address = abituguru_detect();
        if (address < 0)
index e89d572..d30855a 100644 (file)
@@ -1119,8 +1119,6 @@ static struct platform_driver abituguru3_driver = {
        .resume = abituguru3_resume
 };
 
-#ifdef CONFIG_DMI
-
 static int __init abituguru3_dmi_detect(void)
 {
        const char *board_vendor, *board_name;
@@ -1159,15 +1157,6 @@ static int __init abituguru3_dmi_detect(void)
        return 1;
 }
 
-#else /* !CONFIG_DMI */
-
-static inline int abituguru3_dmi_detect(void)
-{
-       return 1;
-}
-
-#endif /* CONFIG_DMI */
-
 /* FIXME: Manual detection should die eventually; we need to collect stable
  *        DMI model names first before we can rely entirely on CONFIG_DMI.
  */
@@ -1216,10 +1205,8 @@ static int __init abituguru3_init(void)
                if (err)
                        return err;
 
-#ifdef CONFIG_DMI
                pr_warn("this motherboard was not detected using DMI. "
                        "Please send the output of \"dmidecode\" to the abituguru3 maintainer (see MAINTAINERS)\n");
-#endif
        }
 
        err = platform_driver_register(&abituguru3_driver);
index fbdc765..b2cacbe 100644 (file)
@@ -62,7 +62,7 @@ static ssize_t adcxx_read(struct device *dev,
 {
        struct spi_device *spi = to_spi_device(dev);
        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adcxx *adc = dev_get_drvdata(&spi->dev);
+       struct adcxx *adc = spi_get_drvdata(spi);
        u8 tx_buf[2];
        u8 rx_buf[2];
        int status;
@@ -105,7 +105,7 @@ static ssize_t adcxx_show_max(struct device *dev,
                struct device_attribute *devattr, char *buf)
 {
        struct spi_device *spi = to_spi_device(dev);
-       struct adcxx *adc = dev_get_drvdata(&spi->dev);
+       struct adcxx *adc = spi_get_drvdata(spi);
        u32 reference;
 
        if (mutex_lock_interruptible(&adc->lock))
@@ -122,7 +122,7 @@ static ssize_t adcxx_set_max(struct device *dev,
        struct device_attribute *devattr, const char *buf, size_t count)
 {
        struct spi_device *spi = to_spi_device(dev);
-       struct adcxx *adc = dev_get_drvdata(&spi->dev);
+       struct adcxx *adc = spi_get_drvdata(spi);
        unsigned long value;
 
        if (strict_strtoul(buf, 10, &value))
@@ -142,7 +142,7 @@ static ssize_t adcxx_show_name(struct device *dev, struct device_attribute
                              *devattr, char *buf)
 {
        struct spi_device *spi = to_spi_device(dev);
-       struct adcxx *adc = dev_get_drvdata(&spi->dev);
+       struct adcxx *adc = spi_get_drvdata(spi);
 
        return sprintf(buf, "adcxx%ds\n", adc->channels);
 }
@@ -182,7 +182,7 @@ static int __devinit adcxx_probe(struct spi_device *spi)
 
        mutex_lock(&adc->lock);
 
-       dev_set_drvdata(&spi->dev, adc);
+       spi_set_drvdata(spi, adc);
 
        for (i = 0; i < 3 + adc->channels; i++) {
                status = device_create_file(&spi->dev, &ad_input[i].dev_attr);
@@ -206,7 +206,7 @@ out_err:
        for (i--; i >= 0; i--)
                device_remove_file(&spi->dev, &ad_input[i].dev_attr);
 
-       dev_set_drvdata(&spi->dev, NULL);
+       spi_set_drvdata(spi, NULL);
        mutex_unlock(&adc->lock);
        kfree(adc);
        return status;
@@ -214,7 +214,7 @@ out_err:
 
 static int __devexit adcxx_remove(struct spi_device *spi)
 {
-       struct adcxx *adc = dev_get_drvdata(&spi->dev);
+       struct adcxx *adc = spi_get_drvdata(spi);
        int i;
 
        mutex_lock(&adc->lock);
@@ -222,7 +222,7 @@ static int __devexit adcxx_remove(struct spi_device *spi)
        for (i = 0; i < 3 + adc->channels; i++)
                device_remove_file(&spi->dev, &ad_input[i].dev_attr);
 
-       dev_set_drvdata(&spi->dev, NULL);
+       spi_set_drvdata(spi, NULL);
        mutex_unlock(&adc->lock);
        kfree(adc);
 
diff --git a/drivers/hwmon/emc6w201.c b/drivers/hwmon/emc6w201.c
new file mode 100644 (file)
index 0000000..e0ef323
--- /dev/null
@@ -0,0 +1,539 @@
+/*
+ * emc6w201.c - Hardware monitoring driver for the SMSC EMC6W201
+ * Copyright (C) 2011  Jean Delvare <khali@linux-fr.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+/*
+ * Addresses to scan
+ */
+
+static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
+
+/*
+ * The EMC6W201 registers
+ */
+
+#define EMC6W201_REG_IN(nr)            (0x20 + (nr))
+#define EMC6W201_REG_TEMP(nr)          (0x26 + (nr))
+#define EMC6W201_REG_FAN(nr)           (0x2C + (nr) * 2)
+#define EMC6W201_REG_COMPANY           0x3E
+#define EMC6W201_REG_VERSTEP           0x3F
+#define EMC6W201_REG_CONFIG            0x40
+#define EMC6W201_REG_IN_LOW(nr)                (0x4A + (nr) * 2)
+#define EMC6W201_REG_IN_HIGH(nr)       (0x4B + (nr) * 2)
+#define EMC6W201_REG_TEMP_LOW(nr)      (0x56 + (nr) * 2)
+#define EMC6W201_REG_TEMP_HIGH(nr)     (0x57 + (nr) * 2)
+#define EMC6W201_REG_FAN_MIN(nr)       (0x62 + (nr) * 2)
+
+enum { input, min, max } subfeature;
+
+/*
+ * Per-device data
+ */
+
+struct emc6w201_data {
+       struct device *hwmon_dev;
+       struct mutex update_lock;
+       char valid; /* zero until following fields are valid */
+       unsigned long last_updated; /* in jiffies */
+
+       /* registers values */
+       u8 in[3][6];
+       s8 temp[3][6];
+       u16 fan[2][5];
+};
+
+/*
+ * Combine LSB and MSB registers in a single value
+ * Locking: must be called with data->update_lock held
+ */
+static u16 emc6w201_read16(struct i2c_client *client, u8 reg)
+{
+       int lsb, msb;
+
+       lsb = i2c_smbus_read_byte_data(client, reg);
+       msb = i2c_smbus_read_byte_data(client, reg + 1);
+       if (lsb < 0 || msb < 0) {
+               dev_err(&client->dev, "16-bit read failed at 0x%02x\n", reg);
+               return 0xFFFF;  /* Arbitrary value */
+       }
+
+       return (msb << 8) | lsb;
+}
+
+/*
+ * Write 16-bit value to LSB and MSB registers
+ * Locking: must be called with data->update_lock held
+ */
+static int emc6w201_write16(struct i2c_client *client, u8 reg, u16 val)
+{
+       int err;
+
+       err = i2c_smbus_write_byte_data(client, reg, val & 0xff);
+       if (!err)
+               err = i2c_smbus_write_byte_data(client, reg + 1, val >> 8);
+       if (err < 0)
+               dev_err(&client->dev, "16-bit write failed at 0x%02x\n", reg);
+
+       return err;
+}
+
+static struct emc6w201_data *emc6w201_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct emc6w201_data *data = i2c_get_clientdata(client);
+       int nr;
+
+       mutex_lock(&data->update_lock);
+
+       if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+               for (nr = 0; nr < 6; nr++) {
+                       data->in[input][nr] =
+                               i2c_smbus_read_byte_data(client,
+                                               EMC6W201_REG_IN(nr));
+                       data->in[min][nr] =
+                               i2c_smbus_read_byte_data(client,
+                                               EMC6W201_REG_IN_LOW(nr));
+                       data->in[max][nr] =
+                               i2c_smbus_read_byte_data(client,
+                                               EMC6W201_REG_IN_HIGH(nr));
+               }
+
+               for (nr = 0; nr < 6; nr++) {
+                       data->temp[input][nr] =
+                               i2c_smbus_read_byte_data(client,
+                                               EMC6W201_REG_TEMP(nr));
+                       data->temp[min][nr] =
+                               i2c_smbus_read_byte_data(client,
+                                               EMC6W201_REG_TEMP_LOW(nr));
+                       data->temp[max][nr] =
+                               i2c_smbus_read_byte_data(client,
+                                               EMC6W201_REG_TEMP_HIGH(nr));
+               }
+
+               for (nr = 0; nr < 5; nr++) {
+                       data->fan[input][nr] =
+                               emc6w201_read16(client,
+                                               EMC6W201_REG_FAN(nr));
+                       data->fan[min][nr] =
+                               emc6w201_read16(client,
+                                               EMC6W201_REG_FAN_MIN(nr));
+               }
+
+               data->last_updated = jiffies;
+               data->valid = 1;
+       }
+
+       mutex_unlock(&data->update_lock);
+
+       return data;
+}
+
+/*
+ * Sysfs callback functions
+ */
+
+static const u16 nominal_mv[6] = { 2500, 1500, 3300, 5000, 1500, 1500 };
+
+static ssize_t show_in(struct device *dev, struct device_attribute *devattr,
+       char *buf)
+{
+       struct emc6w201_data *data = emc6w201_update_device(dev);
+       int sf = to_sensor_dev_attr_2(devattr)->index;
+       int nr = to_sensor_dev_attr_2(devattr)->nr;
+
+       return sprintf(buf, "%u\n",
+                      (unsigned)data->in[sf][nr] * nominal_mv[nr] / 0xC0);
+}
+
+static ssize_t set_in(struct device *dev, struct device_attribute *devattr,
+                     const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct emc6w201_data *data = i2c_get_clientdata(client);
+       int sf = to_sensor_dev_attr_2(devattr)->index;
+       int nr = to_sensor_dev_attr_2(devattr)->nr;
+       int err;
+       long val;
+       u8 reg;
+
+       err = strict_strtol(buf, 10, &val);
+       if (err < 0)
+               return err;
+
+       val = DIV_ROUND_CLOSEST(val * 0xC0, nominal_mv[nr]);
+       reg = (sf == min) ? EMC6W201_REG_IN_LOW(nr)
+                         : EMC6W201_REG_IN_HIGH(nr);
+
+       mutex_lock(&data->update_lock);
+       data->in[sf][nr] = SENSORS_LIMIT(val, 0, 255);
+       err = i2c_smbus_write_byte_data(client, reg, data->in[sf][nr]);
+       mutex_unlock(&data->update_lock);
+
+       return err < 0 ? err : count;
+}
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
+       char *buf)
+{
+       struct emc6w201_data *data = emc6w201_update_device(dev);
+       int sf = to_sensor_dev_attr_2(devattr)->index;
+       int nr = to_sensor_dev_attr_2(devattr)->nr;
+
+       return sprintf(buf, "%d\n", (int)data->temp[sf][nr] * 1000);
+}
+
+static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
+                       const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct emc6w201_data *data = i2c_get_clientdata(client);
+       int sf = to_sensor_dev_attr_2(devattr)->index;
+       int nr = to_sensor_dev_attr_2(devattr)->nr;
+       int err;
+       long val;
+       u8 reg;
+
+       err = strict_strtol(buf, 10, &val);
+       if (err < 0)
+               return err;
+
+       val /= 1000;
+       reg = (sf == min) ? EMC6W201_REG_TEMP_LOW(nr)
+                         : EMC6W201_REG_TEMP_HIGH(nr);
+
+       mutex_lock(&data->update_lock);
+       data->temp[sf][nr] = SENSORS_LIMIT(val, -127, 128);
+       err = i2c_smbus_write_byte_data(client, reg, data->temp[sf][nr]);
+       mutex_unlock(&data->update_lock);
+
+       return err < 0 ? err : count;
+}
+
+static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
+       char *buf)
+{
+       struct emc6w201_data *data = emc6w201_update_device(dev);
+       int sf = to_sensor_dev_attr_2(devattr)->index;
+       int nr = to_sensor_dev_attr_2(devattr)->nr;
+       unsigned rpm;
+
+       if (data->fan[sf][nr] == 0 || data->fan[sf][nr] == 0xFFFF)
+               rpm = 0;
+       else
+               rpm = 5400000U / data->fan[sf][nr];
+
+       return sprintf(buf, "%u\n", rpm);
+}
+
+static ssize_t set_fan(struct device *dev, struct device_attribute *devattr,
+                      const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct emc6w201_data *data = i2c_get_clientdata(client);
+       int sf = to_sensor_dev_attr_2(devattr)->index;
+       int nr = to_sensor_dev_attr_2(devattr)->nr;
+       int err;
+       unsigned long val;
+
+       err = strict_strtoul(buf, 10, &val);
+       if (err < 0)
+               return err;
+
+       if (val == 0) {
+               val = 0xFFFF;
+       } else {
+               val = DIV_ROUND_CLOSEST(5400000U, val);
+               val = SENSORS_LIMIT(val, 0, 0xFFFE);
+       }
+
+       mutex_lock(&data->update_lock);
+       data->fan[sf][nr] = val;
+       err = emc6w201_write16(client, EMC6W201_REG_FAN_MIN(nr),
+                              data->fan[sf][nr]);
+       mutex_unlock(&data->update_lock);
+
+       return err < 0 ? err : count;
+}
+
+static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, input);
+static SENSOR_DEVICE_ATTR_2(in0_min, S_IRUGO | S_IWUSR, show_in, set_in,
+                           0, min);
+static SENSOR_DEVICE_ATTR_2(in0_max, S_IRUGO | S_IWUSR, show_in, set_in,
+                           0, max);
+static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 1, input);
+static SENSOR_DEVICE_ATTR_2(in1_min, S_IRUGO | S_IWUSR, show_in, set_in,
+                           1, min);
+static SENSOR_DEVICE_ATTR_2(in1_max, S_IRUGO | S_IWUSR, show_in, set_in,
+                           1, max);
+static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 2, input);
+static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_in, set_in,
+                           2, min);
+static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_in, set_in,
+                           2, max);
+static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 3, input);
+static SENSOR_DEVICE_ATTR_2(in3_min, S_IRUGO | S_IWUSR, show_in, set_in,
+                           3, min);
+static SENSOR_DEVICE_ATTR_2(in3_max, S_IRUGO | S_IWUSR, show_in, set_in,
+                           3, max);
+static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 4, input);
+static SENSOR_DEVICE_ATTR_2(in4_min, S_IRUGO | S_IWUSR, show_in, set_in,
+                           4, min);
+static SENSOR_DEVICE_ATTR_2(in4_max, S_IRUGO | S_IWUSR, show_in, set_in,
+                           4, max);
+static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, show_in, NULL, 5, input);
+static SENSOR_DEVICE_ATTR_2(in5_min, S_IRUGO | S_IWUSR, show_in, set_in,
+                           5, min);
+static SENSOR_DEVICE_ATTR_2(in5_max, S_IRUGO | S_IWUSR, show_in, set_in,
+                           5, max);
+
+static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, input);
+static SENSOR_DEVICE_ATTR_2(temp1_min, S_IRUGO | S_IWUSR, show_temp, set_temp,
+                           0, min);
+static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+                           0, max);
+static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 1, input);
+static SENSOR_DEVICE_ATTR_2(temp2_min, S_IRUGO | S_IWUSR, show_temp, set_temp,
+                           1, min);
+static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+                           1, max);
+static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 2, input);
+static SENSOR_DEVICE_ATTR_2(temp3_min, S_IRUGO | S_IWUSR, show_temp, set_temp,
+                           2, min);
+static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+                           2, max);
+static SENSOR_DEVICE_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 3, input);
+static SENSOR_DEVICE_ATTR_2(temp4_min, S_IRUGO | S_IWUSR, show_temp, set_temp,
+                           3, min);
+static SENSOR_DEVICE_ATTR_2(temp4_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+                           3, max);
+static SENSOR_DEVICE_ATTR_2(temp5_input, S_IRUGO, show_temp, NULL, 4, input);
+static SENSOR_DEVICE_ATTR_2(temp5_min, S_IRUGO | S_IWUSR, show_temp, set_temp,
+                           4, min);
+static SENSOR_DEVICE_ATTR_2(temp5_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+                           4, max);
+static SENSOR_DEVICE_ATTR_2(temp6_input, S_IRUGO, show_temp, NULL, 5, input);
+static SENSOR_DEVICE_ATTR_2(temp6_min, S_IRUGO | S_IWUSR, show_temp, set_temp,
+                           5, min);
+static SENSOR_DEVICE_ATTR_2(temp6_max, S_IRUGO | S_IWUSR, show_temp, set_temp,
+                           5, max);
+
+static SENSOR_DEVICE_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, input);
+static SENSOR_DEVICE_ATTR_2(fan1_min, S_IRUGO | S_IWUSR, show_fan, set_fan,
+                           0, min);
+static SENSOR_DEVICE_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 1, input);
+static SENSOR_DEVICE_ATTR_2(fan2_min, S_IRUGO | S_IWUSR, show_fan, set_fan,
+                           1, min);
+static SENSOR_DEVICE_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 2, input);
+static SENSOR_DEVICE_ATTR_2(fan3_min, S_IRUGO | S_IWUSR, show_fan, set_fan,
+                           2, min);
+static SENSOR_DEVICE_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 3, input);
+static SENSOR_DEVICE_ATTR_2(fan4_min, S_IRUGO | S_IWUSR, show_fan, set_fan,
+                           3, min);
+static SENSOR_DEVICE_ATTR_2(fan5_input, S_IRUGO, show_fan, NULL, 4, input);
+static SENSOR_DEVICE_ATTR_2(fan5_min, S_IRUGO | S_IWUSR, show_fan, set_fan,
+                           4, min);
+
+static struct attribute *emc6w201_attributes[] = {
+       &sensor_dev_attr_in0_input.dev_attr.attr,
+       &sensor_dev_attr_in0_min.dev_attr.attr,
+       &sensor_dev_attr_in0_max.dev_attr.attr,
+       &sensor_dev_attr_in1_input.dev_attr.attr,
+       &sensor_dev_attr_in1_min.dev_attr.attr,
+       &sensor_dev_attr_in1_max.dev_attr.attr,
+       &sensor_dev_attr_in2_input.dev_attr.attr,
+       &sensor_dev_attr_in2_min.dev_attr.attr,
+       &sensor_dev_attr_in2_max.dev_attr.attr,
+       &sensor_dev_attr_in3_input.dev_attr.attr,
+       &sensor_dev_attr_in3_min.dev_attr.attr,
+       &sensor_dev_attr_in3_max.dev_attr.attr,
+       &sensor_dev_attr_in4_input.dev_attr.attr,
+       &sensor_dev_attr_in4_min.dev_attr.attr,
+       &sensor_dev_attr_in4_max.dev_attr.attr,
+       &sensor_dev_attr_in5_input.dev_attr.attr,
+       &sensor_dev_attr_in5_min.dev_attr.attr,
+       &sensor_dev_attr_in5_max.dev_attr.attr,
+
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp1_min.dev_attr.attr,
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_temp2_input.dev_attr.attr,
+       &sensor_dev_attr_temp2_min.dev_attr.attr,
+       &sensor_dev_attr_temp2_max.dev_attr.attr,
+       &sensor_dev_attr_temp3_input.dev_attr.attr,
+       &sensor_dev_attr_temp3_min.dev_attr.attr,
+       &sensor_dev_attr_temp3_max.dev_attr.attr,
+       &sensor_dev_attr_temp4_input.dev_attr.attr,
+       &sensor_dev_attr_temp4_min.dev_attr.attr,
+       &sensor_dev_attr_temp4_max.dev_attr.attr,
+       &sensor_dev_attr_temp5_input.dev_attr.attr,
+       &sensor_dev_attr_temp5_min.dev_attr.attr,
+       &sensor_dev_attr_temp5_max.dev_attr.attr,
+       &sensor_dev_attr_temp6_input.dev_attr.attr,
+       &sensor_dev_attr_temp6_min.dev_attr.attr,
+       &sensor_dev_attr_temp6_max.dev_attr.attr,
+
+       &sensor_dev_attr_fan1_input.dev_attr.attr,
+       &sensor_dev_attr_fan1_min.dev_attr.attr,
+       &sensor_dev_attr_fan2_input.dev_attr.attr,
+       &sensor_dev_attr_fan2_min.dev_attr.attr,
+       &sensor_dev_attr_fan3_input.dev_attr.attr,
+       &sensor_dev_attr_fan3_min.dev_attr.attr,
+       &sensor_dev_attr_fan4_input.dev_attr.attr,
+       &sensor_dev_attr_fan4_min.dev_attr.attr,
+       &sensor_dev_attr_fan5_input.dev_attr.attr,
+       &sensor_dev_attr_fan5_min.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group emc6w201_group = {
+       .attrs = emc6w201_attributes,
+};
+
+/*
+ * Driver interface
+ */
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int emc6w201_detect(struct i2c_client *client,
+                          struct i2c_board_info *info)
+{
+       struct i2c_adapter *adapter = client->adapter;
+       int company, verstep, config;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return -ENODEV;
+
+       /* Identification */
+       company = i2c_smbus_read_byte_data(client, EMC6W201_REG_COMPANY);
+       if (company != 0x5C)
+               return -ENODEV;
+       verstep = i2c_smbus_read_byte_data(client, EMC6W201_REG_VERSTEP);
+       if (verstep < 0 || (verstep & 0xF0) != 0xB0)
+               return -ENODEV;
+       if ((verstep & 0x0F) > 2) {
+               dev_dbg(&client->dev, "Unknwown EMC6W201 stepping %d\n",
+                       verstep & 0x0F);
+               return -ENODEV;
+       }
+
+       /* Check configuration */
+       config = i2c_smbus_read_byte_data(client, EMC6W201_REG_CONFIG);
+       if ((config & 0xF4) != 0x04)
+               return -ENODEV;
+       if (!(config & 0x01)) {
+               dev_err(&client->dev, "Monitoring not enabled\n");
+               return -ENODEV;
+       }
+
+       strlcpy(info->type, "emc6w201", I2C_NAME_SIZE);
+
+       return 0;
+}
+
+static int emc6w201_probe(struct i2c_client *client,
+                         const struct i2c_device_id *id)
+{
+       struct emc6w201_data *data;
+       int err;
+
+       data = kzalloc(sizeof(struct emc6w201_data), GFP_KERNEL);
+       if (!data) {
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       i2c_set_clientdata(client, data);
+       mutex_init(&data->update_lock);
+
+       /* Create sysfs attribute */
+       err = sysfs_create_group(&client->dev.kobj, &emc6w201_group);
+       if (err)
+               goto exit_free;
+
+       /* Expose as a hwmon device */
+       data->hwmon_dev = hwmon_device_register(&client->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
+               goto exit_remove;
+       }
+
+       return 0;
+
+ exit_remove:
+       sysfs_remove_group(&client->dev.kobj, &emc6w201_group);
+ exit_free:
+       kfree(data);
+ exit:
+       return err;
+}
+
+static int emc6w201_remove(struct i2c_client *client)
+{
+       struct emc6w201_data *data = i2c_get_clientdata(client);
+
+       hwmon_device_unregister(data->hwmon_dev);
+       sysfs_remove_group(&client->dev.kobj, &emc6w201_group);
+       kfree(data);
+
+       return 0;
+}
+
+static const struct i2c_device_id emc6w201_id[] = {
+       { "emc6w201", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, emc6w201_id);
+
+static struct i2c_driver emc6w201_driver = {
+       .class          = I2C_CLASS_HWMON,
+       .driver = {
+               .name   = "emc6w201",
+       },
+       .probe          = emc6w201_probe,
+       .remove         = emc6w201_remove,
+       .id_table       = emc6w201_id,
+       .detect         = emc6w201_detect,
+       .address_list   = normal_i2c,
+};
+
+static int __init sensors_emc6w201_init(void)
+{
+       return i2c_add_driver(&emc6w201_driver);
+}
+module_init(sensors_emc6w201_init);
+
+static void __exit sensors_emc6w201_exit(void)
+{
+       i2c_del_driver(&emc6w201_driver);
+}
+module_exit(sensors_emc6w201_exit);
+
+MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("SMSC EMC6W201 hardware monitoring driver");
+MODULE_LICENSE("GPL");
index ca07a32..a4a94a0 100644 (file)
@@ -48,6 +48,7 @@
 
 #define SIO_FINTEK_ID          0x1934  /* Manufacturers ID */
 #define SIO_F71808E_ID         0x0901  /* Chipset ID */
+#define SIO_F71808A_ID         0x1001  /* Chipset ID */
 #define SIO_F71858_ID          0x0507  /* Chipset ID */
 #define SIO_F71862_ID          0x0601  /* Chipset ID */
 #define SIO_F71869_ID          0x0814  /* Chipset ID */
@@ -107,11 +108,12 @@ static unsigned short force_id;
 module_param(force_id, ushort, 0);
 MODULE_PARM_DESC(force_id, "Override the detected device ID");
 
-enum chips { f71808e, f71858fg, f71862fg, f71869, f71882fg, f71889fg,
+enum chips { f71808e, f71808a, f71858fg, f71862fg, f71869, f71882fg, f71889fg,
             f71889ed, f71889a, f8000, f81865f };
 
 static const char *f71882fg_names[] = {
        "f71808e",
+       "f71808a",
        "f71858fg",
        "f71862fg",
        "f71869", /* Both f71869f and f71869e, reg. compatible and same id */
@@ -125,6 +127,7 @@ static const char *f71882fg_names[] = {
 
 static const char f71882fg_has_in[][F71882FG_MAX_INS] = {
        [f71808e]       = { 1, 1, 1, 1, 1, 1, 0, 1, 1 },
+       [f71808a]       = { 1, 1, 1, 1, 0, 0, 0, 1, 1 },
        [f71858fg]      = { 1, 1, 1, 0, 0, 0, 0, 0, 0 },
        [f71862fg]      = { 1, 1, 1, 1, 1, 1, 1, 1, 1 },
        [f71869]        = { 1, 1, 1, 1, 1, 1, 1, 1, 1 },
@@ -138,6 +141,7 @@ static const char f71882fg_has_in[][F71882FG_MAX_INS] = {
 
 static const char f71882fg_has_in1_alarm[] = {
        [f71808e]       = 0,
+       [f71808a]       = 0,
        [f71858fg]      = 0,
        [f71862fg]      = 0,
        [f71869]        = 0,
@@ -149,8 +153,9 @@ static const char f71882fg_has_in1_alarm[] = {
        [f81865f]       = 1,
 };
 
-static const char f71882fg_has_beep[] = {
+static const char f71882fg_fan_has_beep[] = {
        [f71808e]       = 0,
+       [f71808a]       = 0,
        [f71858fg]      = 0,
        [f71862fg]      = 1,
        [f71869]        = 1,
@@ -164,6 +169,7 @@ static const char f71882fg_has_beep[] = {
 
 static const char f71882fg_nr_fans[] = {
        [f71808e]       = 3,
+       [f71808a]       = 2, /* +1 fan which is monitor + simple pwm only */
        [f71858fg]      = 3,
        [f71862fg]      = 3,
        [f71869]        = 3,
@@ -171,12 +177,27 @@ static const char f71882fg_nr_fans[] = {
        [f71889fg]      = 3,
        [f71889ed]      = 3,
        [f71889a]       = 3,
-       [f8000]         = 3,
+       [f8000]         = 3, /* +1 fan which is monitor only */
        [f81865f]       = 2,
 };
 
+static const char f71882fg_temp_has_beep[] = {
+       [f71808e]       = 0,
+       [f71808a]       = 1,
+       [f71858fg]      = 0,
+       [f71862fg]      = 1,
+       [f71869]        = 1,
+       [f71882fg]      = 1,
+       [f71889fg]      = 1,
+       [f71889ed]      = 1,
+       [f71889a]       = 1,
+       [f8000]         = 0,
+       [f81865f]       = 1,
+};
+
 static const char f71882fg_nr_temps[] = {
        [f71808e]       = 2,
+       [f71808a]       = 2,
        [f71858fg]      = 3,
        [f71862fg]      = 3,
        [f71869]        = 3,
@@ -301,6 +322,10 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr,
        char *buf);
 static ssize_t store_pwm(struct device *dev, struct device_attribute *devattr,
        const char *buf, size_t count);
+static ssize_t show_simple_pwm(struct device *dev,
+       struct device_attribute *devattr, char *buf);
+static ssize_t store_simple_pwm(struct device *dev,
+       struct device_attribute *devattr, const char *buf, size_t count);
 static ssize_t show_pwm_enable(struct device *dev,
        struct device_attribute *devattr, char *buf);
 static ssize_t store_pwm_enable(struct device *dev,
@@ -550,6 +575,14 @@ static struct sensor_device_attribute_2 fxxxx_fan_attr[4][6] = { {
                      show_pwm_interpolate, store_pwm_interpolate, 0, 3),
 } };
 
+/* Attr for the third fan of the f71808a, which only has manual pwm */
+static struct sensor_device_attribute_2 f71808a_fan3_attr[] = {
+       SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2),
+       SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2),
+       SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR,
+                     show_simple_pwm, store_simple_pwm, 0, 2),
+};
+
 /* Attr for models which can beep on Fan alarm */
 static struct sensor_device_attribute_2 fxxxx_fan_beep_attr[] = {
        SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep,
@@ -1146,12 +1179,13 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
                        data->temp_type[3] = (reg & 0x08) ? 2 : 4;
                }
 
-               if (f71882fg_has_beep[data->type]) {
+               if (f71882fg_fan_has_beep[data->type])
                        data->fan_beep = f71882fg_read8(data,
                                                F71882FG_REG_FAN_BEEP);
+
+               if (f71882fg_temp_has_beep[data->type])
                        data->temp_beep = f71882fg_read8(data,
                                                F71882FG_REG_TEMP_BEEP);
-               }
 
                data->pwm_enable = f71882fg_read8(data,
                                                  F71882FG_REG_PWM_ENABLE);
@@ -1232,7 +1266,13 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
                        data->pwm[nr] =
                            f71882fg_read8(data, F71882FG_REG_PWM(nr));
                }
-               /* The f8000 can monitor 1 more fan, but has no pwm for it */
+               /* Some models have 1 more fan with limited capabilities */
+               if (data->type == f71808a) {
+                       data->fan[2] = f71882fg_read16(data,
+                                               F71882FG_REG_FAN(2));
+                       data->pwm[2] = f71882fg_read8(data,
+                                                       F71882FG_REG_PWM(2));
+               }
                if (data->type == f8000)
                        data->fan[3] = f71882fg_read16(data,
                                                F71882FG_REG_FAN(3));
@@ -1722,6 +1762,38 @@ leave:
        return count;
 }
 
+static ssize_t show_simple_pwm(struct device *dev,
+                              struct device_attribute *devattr, char *buf)
+{
+       struct f71882fg_data *data = f71882fg_update_device(dev);
+       int val, nr = to_sensor_dev_attr_2(devattr)->index;
+
+       val = data->pwm[nr];
+       return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t store_simple_pwm(struct device *dev,
+                               struct device_attribute *devattr,
+                               const char *buf, size_t count)
+{
+       struct f71882fg_data *data = dev_get_drvdata(dev);
+       int err, nr = to_sensor_dev_attr_2(devattr)->index;
+       long val;
+
+       err = strict_strtol(buf, 10, &val);
+       if (err)
+               return err;
+
+       val = SENSORS_LIMIT(val, 0, 255);
+
+       mutex_lock(&data->update_lock);
+       f71882fg_write8(data, F71882FG_REG_PWM(nr), val);
+       data->pwm[nr] = val;
+       mutex_unlock(&data->update_lock);
+
+       return count;
+}
+
 static ssize_t show_pwm_enable(struct device *dev,
                               struct device_attribute *devattr, char *buf)
 {
@@ -2140,7 +2212,7 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
                if (err)
                        goto exit_unregister_sysfs;
 
-               if (f71882fg_has_beep[data->type]) {
+               if (f71882fg_temp_has_beep[data->type]) {
                        err = f71882fg_create_sysfs_files(pdev,
                                        &fxxxx_temp_beep_attr[0][0],
                                        ARRAY_SIZE(fxxxx_temp_beep_attr[0])
@@ -2169,6 +2241,7 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
        if (start_reg & 0x02) {
                switch (data->type) {
                case f71808e:
+               case f71808a:
                case f71869:
                        /* These always have signed auto point temps */
                        data->auto_point_temp_signed = 1;
@@ -2221,7 +2294,7 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
                if (err)
                        goto exit_unregister_sysfs;
 
-               if (f71882fg_has_beep[data->type]) {
+               if (f71882fg_fan_has_beep[data->type]) {
                        err = f71882fg_create_sysfs_files(pdev,
                                        fxxxx_fan_beep_attr, nr_fans);
                        if (err)
@@ -2230,6 +2303,7 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
 
                switch (data->type) {
                case f71808e:
+               case f71808a:
                case f71869:
                case f71889fg:
                case f71889ed:
@@ -2255,6 +2329,16 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
                }
 
                switch (data->type) {
+               case f71808a:
+                       err = f71882fg_create_sysfs_files(pdev,
+                               &fxxxx_auto_pwm_attr[0][0],
+                               ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
+                       if (err)
+                               goto exit_unregister_sysfs;
+                       err = f71882fg_create_sysfs_files(pdev,
+                                       f71808a_fan3_attr,
+                                       ARRAY_SIZE(f71808a_fan3_attr));
+                       break;
                case f71862fg:
                        err = f71882fg_create_sysfs_files(pdev,
                                        f71862fg_auto_pwm_attr,
@@ -2343,7 +2427,7 @@ static int f71882fg_remove(struct platform_device *pdev)
                                &fxxxx_temp_attr[0][0],
                                ARRAY_SIZE(fxxxx_temp_attr[0]) * nr_temps);
                }
-               if (f71882fg_has_beep[data->type]) {
+               if (f71882fg_temp_has_beep[data->type]) {
                        f71882fg_remove_sysfs_files(pdev,
                               &fxxxx_temp_beep_attr[0][0],
                               ARRAY_SIZE(fxxxx_temp_beep_attr[0]) * nr_temps);
@@ -2366,12 +2450,20 @@ static int f71882fg_remove(struct platform_device *pdev)
                f71882fg_remove_sysfs_files(pdev, &fxxxx_fan_attr[0][0],
                                ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans);
 
-               if (f71882fg_has_beep[data->type]) {
+               if (f71882fg_fan_has_beep[data->type]) {
                        f71882fg_remove_sysfs_files(pdev,
                                        fxxxx_fan_beep_attr, nr_fans);
                }
 
                switch (data->type) {
+               case f71808a:
+                       f71882fg_remove_sysfs_files(pdev,
+                               &fxxxx_auto_pwm_attr[0][0],
+                               ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
+                       f71882fg_remove_sysfs_files(pdev,
+                                       f71808a_fan3_attr,
+                                       ARRAY_SIZE(f71808a_fan3_attr));
+                       break;
                case f71862fg:
                        f71882fg_remove_sysfs_files(pdev,
                                        f71862fg_auto_pwm_attr,
@@ -2424,6 +2516,9 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
        case SIO_F71808E_ID:
                sio_data->type = f71808e;
                break;
+       case SIO_F71808A_ID:
+               sio_data->type = f71808a;
+               break;
        case SIO_F71858_ID:
                sio_data->type = f71858fg;
                break;
diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
new file mode 100644 (file)
index 0000000..523f8fb
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * fam15h_power.c - AMD Family 15h processor power monitoring
+ *
+ * Copyright (c) 2011 Advanced Micro Devices, Inc.
+ * Author: Andreas Herrmann <andreas.herrmann3@amd.com>
+ *
+ *
+ * This driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <asm/processor.h>
+
+MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor");
+MODULE_AUTHOR("Andreas Herrmann <andreas.herrmann3@amd.com>");
+MODULE_LICENSE("GPL");
+
+/* D18F3 */
+#define REG_NORTHBRIDGE_CAP            0xe8
+
+/* D18F4 */
+#define REG_PROCESSOR_TDP              0x1b8
+
+/* D18F5 */
+#define REG_TDP_RUNNING_AVERAGE                0xe0
+#define REG_TDP_LIMIT3                 0xe8
+
+struct fam15h_power_data {
+       struct device *hwmon_dev;
+       unsigned int tdp_to_watts;
+       unsigned int base_tdp;
+       unsigned int processor_pwr_watts;
+};
+
+static ssize_t show_power(struct device *dev,
+                         struct device_attribute *attr, char *buf)
+{
+       u32 val, tdp_limit, running_avg_range;
+       s32 running_avg_capture;
+       u64 curr_pwr_watts;
+       struct pci_dev *f4 = to_pci_dev(dev);
+       struct fam15h_power_data *data = dev_get_drvdata(dev);
+
+       pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+                                 REG_TDP_RUNNING_AVERAGE, &val);
+       running_avg_capture = (val >> 4) & 0x3fffff;
+       running_avg_capture = sign_extend32(running_avg_capture, 22);
+       running_avg_range = val & 0xf;
+
+       pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+                                 REG_TDP_LIMIT3, &val);
+
+       tdp_limit = val >> 16;
+       curr_pwr_watts = tdp_limit + data->base_tdp -
+               (s32)(running_avg_capture >> (running_avg_range + 1));
+       curr_pwr_watts *= data->tdp_to_watts;
+
+       /*
+        * Convert to microWatt
+        *
+        * power is in Watt provided as fixed point integer with
+        * scaling factor 1/(2^16).  For conversion we use
+        * (10^6)/(2^16) = 15625/(2^10)
+        */
+       curr_pwr_watts = (curr_pwr_watts * 15625) >> 10;
+       return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts);
+}
+static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
+
+static ssize_t show_power_crit(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       struct fam15h_power_data *data = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%u\n", data->processor_pwr_watts);
+}
+static DEVICE_ATTR(power1_crit, S_IRUGO, show_power_crit, NULL);
+
+static ssize_t show_name(struct device *dev,
+                        struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "fam15h_power\n");
+}
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static struct attribute *fam15h_power_attrs[] = {
+       &dev_attr_power1_input.attr,
+       &dev_attr_power1_crit.attr,
+       &dev_attr_name.attr,
+       NULL
+};
+
+static const struct attribute_group fam15h_power_attr_group = {
+       .attrs  = fam15h_power_attrs,
+};
+
+static bool __devinit fam15h_power_is_internal_node0(struct pci_dev *f4)
+{
+       u32 val;
+
+       pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 3),
+                                 REG_NORTHBRIDGE_CAP, &val);
+       if ((val & BIT(29)) && ((val >> 30) & 3))
+               return false;
+
+       return true;
+}
+
+static void __devinit fam15h_power_init_data(struct pci_dev *f4,
+                                            struct fam15h_power_data *data)
+{
+       u32 val;
+       u64 tmp;
+
+       pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val);
+       data->base_tdp = val >> 16;
+       tmp = val & 0xffff;
+
+       pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
+                                 REG_TDP_LIMIT3, &val);
+
+       data->tdp_to_watts = ((val & 0x3ff) << 6) | ((val >> 10) & 0x3f);
+       tmp *= data->tdp_to_watts;
+
+       /* result not allowed to be >= 256W */
+       if ((tmp >> 16) >= 256)
+               dev_warn(&f4->dev, "Bogus value for ProcessorPwrWatts "
+                        "(processor_pwr_watts>=%u)\n",
+                        (unsigned int) (tmp >> 16));
+
+       /* convert to microWatt */
+       data->processor_pwr_watts = (tmp * 15625) >> 10;
+}
+
+static int __devinit fam15h_power_probe(struct pci_dev *pdev,
+                                       const struct pci_device_id *id)
+{
+       struct fam15h_power_data *data;
+       struct device *dev;
+       int err;
+
+       if (!fam15h_power_is_internal_node0(pdev)) {
+               err = -ENODEV;
+               goto exit;
+       }
+
+       data = kzalloc(sizeof(struct fam15h_power_data), GFP_KERNEL);
+       if (!data) {
+               err = -ENOMEM;
+               goto exit;
+       }
+       fam15h_power_init_data(pdev, data);
+       dev = &pdev->dev;
+
+       dev_set_drvdata(dev, data);
+       err = sysfs_create_group(&dev->kobj, &fam15h_power_attr_group);
+       if (err)
+               goto exit_free_data;
+
+       data->hwmon_dev = hwmon_device_register(dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
+               goto exit_remove_group;
+       }
+
+       return 0;
+
+exit_remove_group:
+       sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group);
+exit_free_data:
+       kfree(data);
+exit:
+       return err;
+}
+
+static void __devexit fam15h_power_remove(struct pci_dev *pdev)
+{
+       struct device *dev;
+       struct fam15h_power_data *data;
+
+       dev = &pdev->dev;
+       data = dev_get_drvdata(dev);
+       hwmon_device_unregister(data->hwmon_dev);
+       sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group);
+       dev_set_drvdata(dev, NULL);
+       kfree(data);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(fam15h_power_id_table) = {
+       { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
+       {}
+};
+MODULE_DEVICE_TABLE(pci, fam15h_power_id_table);
+
+static struct pci_driver fam15h_power_driver = {
+       .name = "fam15h_power",
+       .id_table = fam15h_power_id_table,
+       .probe = fam15h_power_probe,
+       .remove = __devexit_p(fam15h_power_remove),
+};
+
+static int __init fam15h_power_init(void)
+{
+       return pci_register_driver(&fam15h_power_driver);
+}
+
+static void __exit fam15h_power_exit(void)
+{
+       pci_unregister_driver(&fam15h_power_driver);
+}
+
+module_init(fam15h_power_init)
+module_exit(fam15h_power_exit)
index bc6e2ab..537409d 100644 (file)
@@ -523,7 +523,7 @@ static void aem_delete(struct aem_data *data)
        aem_remove_sensors(data);
        hwmon_device_unregister(data->hwmon_dev);
        ipmi_destroy_user(data->ipmi.user);
-       dev_set_drvdata(&data->pdev->dev, NULL);
+       platform_set_drvdata(data->pdev, NULL);
        platform_device_unregister(data->pdev);
        aem_idr_put(data->id);
        kfree(data);
@@ -594,7 +594,7 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle)
        if (res)
                goto ipmi_err;
 
-       dev_set_drvdata(&data->pdev->dev, data);
+       platform_set_drvdata(data->pdev, data);
 
        /* Set up IPMI interface */
        if (aem_init_ipmi_data(&data->ipmi, probe->interface,
@@ -630,7 +630,7 @@ sensor_err:
 hwmon_reg_err:
        ipmi_destroy_user(data->ipmi.user);
 ipmi_err:
-       dev_set_drvdata(&data->pdev->dev, NULL);
+       platform_set_drvdata(data->pdev, NULL);
        platform_device_unregister(data->pdev);
 dev_err:
        aem_idr_put(data->id);
@@ -727,7 +727,7 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe,
        if (res)
                goto ipmi_err;
 
-       dev_set_drvdata(&data->pdev->dev, data);
+       platform_set_drvdata(data->pdev, data);
 
        /* Set up IPMI interface */
        if (aem_init_ipmi_data(&data->ipmi, probe->interface,
@@ -763,7 +763,7 @@ sensor_err:
 hwmon_reg_err:
        ipmi_destroy_user(data->ipmi.user);
 ipmi_err:
-       dev_set_drvdata(&data->pdev->dev, NULL);
+       platform_set_drvdata(data->pdev, NULL);
        platform_device_unregister(data->pdev);
 dev_err:
        aem_idr_put(data->id);
index 316b648..bb6405b 100644 (file)
@@ -77,15 +77,13 @@ static struct platform_device *pdev;
 #define        DEVID   0x20    /* Register: Device ID */
 #define        DEVREV  0x22    /* Register: Device Revision */
 
-static inline int
-superio_inb(int reg)
+static inline int superio_inb(int reg)
 {
        outb(reg, REG);
        return inb(VAL);
 }
 
-static inline void
-superio_outb(int reg, int val)
+static inline void superio_outb(int reg, int val)
 {
        outb(reg, REG);
        outb(val, VAL);
@@ -101,27 +99,32 @@ static int superio_inw(int reg)
        return val;
 }
 
-static inline void
-superio_select(int ldn)
+static inline void superio_select(int ldn)
 {
        outb(DEV, REG);
        outb(ldn, VAL);
 }
 
-static inline void
-superio_enter(void)
+static inline int superio_enter(void)
 {
+       /*
+        * Try to reserve REG and REG + 1 for exclusive access.
+        */
+       if (!request_muxed_region(REG, 2, DRVNAME))
+               return -EBUSY;
+
        outb(0x87, REG);
        outb(0x01, REG);
        outb(0x55, REG);
        outb(0x55, REG);
+       return 0;
 }
 
-static inline void
-superio_exit(void)
+static inline void superio_exit(void)
 {
        outb(0x02, REG);
        outb(0x02, VAL);
+       release_region(REG, 2);
 }
 
 /* Logical device 4 registers */
@@ -1542,11 +1545,15 @@ static const struct attribute_group it87_group_label = {
 static int __init it87_find(unsigned short *address,
        struct it87_sio_data *sio_data)
 {
-       int err = -ENODEV;
+       int err;
        u16 chip_type;
        const char *board_vendor, *board_name;
 
-       superio_enter();
+       err = superio_enter();
+       if (err)
+               return err;
+
+       err = -ENODEV;
        chip_type = force_id ? force_id : superio_inw(DEVID);
 
        switch (chip_type) {
index 9349912..02cebb7 100644 (file)
@@ -213,7 +213,7 @@ static const struct dev_pm_ops jc42_dev_pm_ops = {
 
 /* This is the driver that will be inserted */
 static struct i2c_driver jc42_driver = {
-       .class          = I2C_CLASS_HWMON,
+       .class          = I2C_CLASS_SPD,
        .driver = {
                .name   = "jc42",
                .pm = JC42_DEV_PM_OPS,
index 82bf65a..41aa6a3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * k10temp.c - AMD Family 10h/11h/12h/14h processor hardware monitoring
+ * k10temp.c - AMD Family 10h/11h/12h/14h/15h processor hardware monitoring
  *
  * Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de>
  *
@@ -25,7 +25,7 @@
 #include <linux/pci.h>
 #include <asm/processor.h>
 
-MODULE_DESCRIPTION("AMD Family 10h/11h/12h/14h CPU core temperature monitor");
+MODULE_DESCRIPTION("AMD Family 10h+ CPU core temperature monitor");
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_LICENSE("GPL");
 
@@ -173,7 +173,7 @@ static int __devinit k10temp_probe(struct pci_dev *pdev,
                err = PTR_ERR(hwmon_dev);
                goto exit_remove;
        }
-       dev_set_drvdata(&pdev->dev, hwmon_dev);
+       pci_set_drvdata(pdev, hwmon_dev);
 
        if (unreliable && force)
                dev_warn(&pdev->dev,
@@ -194,7 +194,7 @@ exit:
 
 static void __devexit k10temp_remove(struct pci_dev *pdev)
 {
-       hwmon_device_unregister(dev_get_drvdata(&pdev->dev));
+       hwmon_device_unregister(pci_get_drvdata(pdev));
        device_remove_file(&pdev->dev, &dev_attr_name);
        device_remove_file(&pdev->dev, &dev_attr_temp1_input);
        device_remove_file(&pdev->dev, &dev_attr_temp1_max);
@@ -202,13 +202,14 @@ static void __devexit k10temp_remove(struct pci_dev *pdev)
                           &sensor_dev_attr_temp1_crit.dev_attr);
        device_remove_file(&pdev->dev,
                           &sensor_dev_attr_temp1_crit_hyst.dev_attr);
-       dev_set_drvdata(&pdev->dev, NULL);
+       pci_set_drvdata(pdev, NULL);
 }
 
 static const struct pci_device_id k10temp_id_table[] = {
        { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) },
        { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) },
        { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) },
+       { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) },
        {}
 };
 MODULE_DEVICE_TABLE(pci, k10temp_id_table);
index 418496f..b923bc2 100644 (file)
@@ -252,7 +252,7 @@ static int __devinit k8temp_probe(struct pci_dev *pdev,
 
        data->name = "k8temp";
        mutex_init(&data->update_lock);
-       dev_set_drvdata(&pdev->dev, data);
+       pci_set_drvdata(pdev, data);
 
        /* Register sysfs hooks */
        err = device_create_file(&pdev->dev,
@@ -307,7 +307,7 @@ exit_remove:
                           &sensor_dev_attr_temp4_input.dev_attr);
        device_remove_file(&pdev->dev, &dev_attr_name);
 exit_free:
-       dev_set_drvdata(&pdev->dev, NULL);
+       pci_set_drvdata(pdev, NULL);
        kfree(data);
 exit:
        return err;
@@ -315,7 +315,7 @@ exit:
 
 static void __devexit k8temp_remove(struct pci_dev *pdev)
 {
-       struct k8temp_data *data = dev_get_drvdata(&pdev->dev);
+       struct k8temp_data *data = pci_get_drvdata(pdev);
 
        hwmon_device_unregister(data->hwmon_dev);
        device_remove_file(&pdev->dev,
@@ -327,7 +327,7 @@ static void __devexit k8temp_remove(struct pci_dev *pdev)
        device_remove_file(&pdev->dev,
                           &sensor_dev_attr_temp4_input.dev_attr);
        device_remove_file(&pdev->dev, &dev_attr_name);
-       dev_set_drvdata(&pdev->dev, NULL);
+       pci_set_drvdata(pdev, NULL);
        kfree(data);
 }
 
index 3b84fb5..c274ea2 100644 (file)
@@ -58,7 +58,7 @@ static ssize_t lm70_sense_temp(struct device *dev,
        int status, val = 0;
        u8 rxbuf[2];
        s16 raw=0;
-       struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev);
+       struct lm70 *p_lm70 = spi_get_drvdata(spi);
 
        if (mutex_lock_interruptible(&p_lm70->lock))
                return -ERESTARTSYS;
@@ -163,7 +163,7 @@ static int __devinit lm70_probe(struct spi_device *spi)
                status = PTR_ERR(p_lm70->hwmon_dev);
                goto out_dev_reg_failed;
        }
-       dev_set_drvdata(&spi->dev, p_lm70);
+       spi_set_drvdata(spi, p_lm70);
 
        if ((status = device_create_file(&spi->dev, &dev_attr_temp1_input))
         || (status = device_create_file(&spi->dev, &dev_attr_name))) {
@@ -177,19 +177,19 @@ out_dev_create_file_failed:
        device_remove_file(&spi->dev, &dev_attr_temp1_input);
        hwmon_device_unregister(p_lm70->hwmon_dev);
 out_dev_reg_failed:
-       dev_set_drvdata(&spi->dev, NULL);
+       spi_set_drvdata(spi, NULL);
        kfree(p_lm70);
        return status;
 }
 
 static int __devexit lm70_remove(struct spi_device *spi)
 {
-       struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev);
+       struct lm70 *p_lm70 = spi_get_drvdata(spi);
 
        device_remove_file(&spi->dev, &dev_attr_temp1_input);
        device_remove_file(&spi->dev, &dev_attr_name);
        hwmon_device_unregister(p_lm70->hwmon_dev);
-       dev_set_drvdata(&spi->dev, NULL);
+       spi_set_drvdata(spi, NULL);
        kfree(p_lm70);
 
        return 0;
index 9a11532..ece3aaf 100644 (file)
 #include <linux/err.h>
 
 /*
- * Addresses to scan. There are four disjoint possibilities, by pin config.
- */
-
-static const unsigned short normal_i2c[] = {0x1b, 0x1f, 0x48, 0x4b,
-                                               I2C_CLIENT_END};
-
-/*
  * Insmod parameters
  */
 
@@ -114,8 +107,6 @@ module_param(clock, int, S_IRUGO);
 
 static int max6650_probe(struct i2c_client *client,
                         const struct i2c_device_id *id);
-static int max6650_detect(struct i2c_client *client,
-                         struct i2c_board_info *info);
 static int max6650_init_client(struct i2c_client *client);
 static int max6650_remove(struct i2c_client *client);
 static struct max6650_data *max6650_update_device(struct device *dev);
@@ -125,21 +116,19 @@ static struct max6650_data *max6650_update_device(struct device *dev);
  */
 
 static const struct i2c_device_id max6650_id[] = {
-       { "max6650", 0 },
+       { "max6650", 1 },
+       { "max6651", 4 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, max6650_id);
 
 static struct i2c_driver max6650_driver = {
-       .class          = I2C_CLASS_HWMON,
        .driver = {
                .name   = "max6650",
        },
        .probe          = max6650_probe,
        .remove         = max6650_remove,
        .id_table       = max6650_id,
-       .detect         = max6650_detect,
-       .address_list   = normal_i2c,
 };
 
 /*
@@ -150,6 +139,7 @@ struct max6650_data
 {
        struct device *hwmon_dev;
        struct mutex update_lock;
+       int nr_fans;
        char valid; /* zero until following fields are valid */
        unsigned long last_updated; /* in jiffies */
 
@@ -501,9 +491,6 @@ static mode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a,
 
 static struct attribute *max6650_attrs[] = {
        &sensor_dev_attr_fan1_input.dev_attr.attr,
-       &sensor_dev_attr_fan2_input.dev_attr.attr,
-       &sensor_dev_attr_fan3_input.dev_attr.attr,
-       &sensor_dev_attr_fan4_input.dev_attr.attr,
        &dev_attr_fan1_target.attr,
        &dev_attr_fan1_div.attr,
        &dev_attr_pwm1_enable.attr,
@@ -521,42 +508,21 @@ static struct attribute_group max6650_attr_grp = {
        .is_visible = max6650_attrs_visible,
 };
 
+static struct attribute *max6651_attrs[] = {
+       &sensor_dev_attr_fan2_input.dev_attr.attr,
+       &sensor_dev_attr_fan3_input.dev_attr.attr,
+       &sensor_dev_attr_fan4_input.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group max6651_attr_grp = {
+       .attrs = max6651_attrs,
+};
+
 /*
  * Real code
  */
 
-/* Return 0 if detection is successful, -ENODEV otherwise */
-static int max6650_detect(struct i2c_client *client,
-                         struct i2c_board_info *info)
-{
-       struct i2c_adapter *adapter = client->adapter;
-       int address = client->addr;
-
-       dev_dbg(&adapter->dev, "max6650_detect called\n");
-
-       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
-               dev_dbg(&adapter->dev, "max6650: I2C bus doesn't support "
-                                       "byte read mode, skipping.\n");
-               return -ENODEV;
-       }
-
-       if (((i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG) & 0xC0)
-           ||(i2c_smbus_read_byte_data(client, MAX6650_REG_GPIO_STAT) & 0xE0)
-           ||(i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN) & 0xE0)
-           ||(i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM) & 0xE0)
-           ||(i2c_smbus_read_byte_data(client, MAX6650_REG_COUNT) & 0xFC))) {
-               dev_dbg(&adapter->dev,
-                       "max6650: detection failed at 0x%02x.\n", address);
-               return -ENODEV;
-       }
-
-       dev_info(&adapter->dev, "max6650: chip found at 0x%02x.\n", address);
-
-       strlcpy(info->type, "max6650", I2C_NAME_SIZE);
-
-       return 0;
-}
-
 static int max6650_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
@@ -570,6 +536,7 @@ static int max6650_probe(struct i2c_client *client,
 
        i2c_set_clientdata(client, data);
        mutex_init(&data->update_lock);
+       data->nr_fans = id->driver_data;
 
        /*
         * Initialize the max6650 chip
@@ -581,6 +548,12 @@ static int max6650_probe(struct i2c_client *client,
        err = sysfs_create_group(&client->dev.kobj, &max6650_attr_grp);
        if (err)
                goto err_free;
+       /* 3 additional fan inputs for the MAX6651 */
+       if (data->nr_fans == 4) {
+               err = sysfs_create_group(&client->dev.kobj, &max6651_attr_grp);
+               if (err)
+                       goto err_remove;
+       }
 
        data->hwmon_dev = hwmon_device_register(&client->dev);
        if (!IS_ERR(data->hwmon_dev))
@@ -588,6 +561,9 @@ static int max6650_probe(struct i2c_client *client,
 
        err = PTR_ERR(data->hwmon_dev);
        dev_err(&client->dev, "error registering hwmon device.\n");
+       if (data->nr_fans == 4)
+               sysfs_remove_group(&client->dev.kobj, &max6651_attr_grp);
+err_remove:
        sysfs_remove_group(&client->dev.kobj, &max6650_attr_grp);
 err_free:
        kfree(data);
@@ -598,8 +574,10 @@ static int max6650_remove(struct i2c_client *client)
 {
        struct max6650_data *data = i2c_get_clientdata(client);
 
-       sysfs_remove_group(&client->dev.kobj, &max6650_attr_grp);
        hwmon_device_unregister(data->hwmon_dev);
+       if (data->nr_fans == 4)
+               sysfs_remove_group(&client->dev.kobj, &max6651_attr_grp);
+       sysfs_remove_group(&client->dev.kobj, &max6650_attr_grp);
        kfree(data);
        return 0;
 }
@@ -712,7 +690,7 @@ static struct max6650_data *max6650_update_device(struct device *dev)
                                                       MAX6650_REG_SPEED);
                data->config = i2c_smbus_read_byte_data(client,
                                                        MAX6650_REG_CONFIG);
-               for (i = 0; i < 4; i++) {
+               for (i = 0; i < data->nr_fans; i++) {
                        data->tach[i] = i2c_smbus_read_byte_data(client,
                                                                 tach_reg[i]);
                }
index 9a51dcc..020c872 100644 (file)
@@ -52,6 +52,9 @@
 #define SCH5627_COMPANY_ID             0x5c
 #define SCH5627_PRIMARY_ID             0xa0
 
+#define SCH5627_CMD_READ               0x02
+#define SCH5627_CMD_WRITE              0x03
+
 #define SCH5627_REG_BUILD_CODE         0x39
 #define SCH5627_REG_BUILD_ID           0x3a
 #define SCH5627_REG_HWMON_ID           0x3c
@@ -94,11 +97,13 @@ static const char * const SCH5627_IN_LABELS[SCH5627_NO_IN] = {
 struct sch5627_data {
        unsigned short addr;
        struct device *hwmon_dev;
+       u8 control;
        u8 temp_max[SCH5627_NO_TEMPS];
        u8 temp_crit[SCH5627_NO_TEMPS];
        u16 fan_min[SCH5627_NO_FANS];
 
        struct mutex update_lock;
+       unsigned long last_battery;     /* In jiffies */
        char valid;                     /* !=0 if following fields are valid */
        unsigned long last_updated;     /* In jiffies */
        u16 temp[SCH5627_NO_TEMPS];
@@ -140,7 +145,7 @@ static inline void superio_exit(int base)
        release_region(base, 2);
 }
 
-static int sch5627_read_virtual_reg(struct sch5627_data *data, u16 reg)
+static int sch5627_send_cmd(struct sch5627_data *data, u8 cmd, u16 reg, u8 v)
 {
        u8 val;
        int i;
@@ -163,10 +168,14 @@ static int sch5627_read_virtual_reg(struct sch5627_data *data, u16 reg)
        outb(0x80, data->addr + 3);
 
        /* Write Request Packet Header */
-       outb(0x02, data->addr + 4); /* Access Type: VREG read */
+       outb(cmd, data->addr + 4); /* VREG Access Type read:0x02 write:0x03 */
        outb(0x01, data->addr + 5); /* # of Entries: 1 Byte (8-bit) */
        outb(0x04, data->addr + 2); /* Mailbox AP to first data entry loc. */
 
+       /* Write Value field */
+       if (cmd == SCH5627_CMD_WRITE)
+               outb(v, data->addr + 4);
+
        /* Write Address field */
        outb(reg & 0xff, data->addr + 6);
        outb(reg >> 8, data->addr + 7);
@@ -224,8 +233,22 @@ static int sch5627_read_virtual_reg(struct sch5627_data *data, u16 reg)
         * But if we do that things don't work, so let's not.
         */
 
-       /* Read Data from Mailbox */
-       return inb(data->addr + 4);
+       /* Read Value field */
+       if (cmd == SCH5627_CMD_READ)
+               return inb(data->addr + 4);
+
+       return 0;
+}
+
+static int sch5627_read_virtual_reg(struct sch5627_data *data, u16 reg)
+{
+       return sch5627_send_cmd(data, SCH5627_CMD_READ, reg, 0);
+}
+
+static int sch5627_write_virtual_reg(struct sch5627_data *data,
+                                    u16 reg, u8 val)
+{
+       return sch5627_send_cmd(data, SCH5627_CMD_WRITE, reg, val);
 }
 
 static int sch5627_read_virtual_reg16(struct sch5627_data *data, u16 reg)
@@ -272,6 +295,13 @@ static struct sch5627_data *sch5627_update_device(struct device *dev)
 
        mutex_lock(&data->update_lock);
 
+       /* Trigger a Vbat voltage measurement every 5 minutes */
+       if (time_after(jiffies, data->last_battery + 300 * HZ)) {
+               sch5627_write_virtual_reg(data, SCH5627_REG_CTRL,
+                                         data->control | 0x10);
+               data->last_battery = jiffies;
+       }
+
        /* Cache the values for 1 second */
        if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
                for (i = 0; i < SCH5627_NO_TEMPS; i++) {
@@ -696,11 +726,17 @@ static int __devinit sch5627_probe(struct platform_device *pdev)
                err = val;
                goto error;
        }
-       if (!(val & 0x01)) {
+       data->control = val;
+       if (!(data->control & 0x01)) {
                pr_err("hardware monitoring not enabled\n");
                err = -ENODEV;
                goto error;
        }
+       /* Trigger a Vbat voltage measurement, so that we get a valid reading
+          the first time we read Vbat */
+       sch5627_write_virtual_reg(data, SCH5627_REG_CTRL,
+                                 data->control | 0x10);
+       data->last_battery = jiffies;
 
        /*
         * Read limits, we do this only once as reading a register on
index 1f36c63..27a6271 100644 (file)
@@ -258,7 +258,7 @@ static int __devinit env_probe(struct platform_device *op)
                goto out_sysfs_remove_group;
        }
 
-       dev_set_drvdata(&op->dev, p);
+       platform_set_drvdata(op, p);
        err = 0;
 
 out:
@@ -277,7 +277,7 @@ out_free:
 
 static int __devexit env_remove(struct platform_device *op)
 {
-       struct env *p = dev_get_drvdata(&op->dev);
+       struct env *p = platform_get_drvdata(op);
 
        if (p) {
                sysfs_remove_group(&op->dev.kobj, &env_group);
index f1e3ff5..a6c652e 100644 (file)
@@ -409,7 +409,7 @@ void i2c_unlock_adapter(struct i2c_adapter *);
 /* i2c adapter classes (bitmask) */
 #define I2C_CLASS_HWMON                (1<<0)  /* lm_sensors, ... */
 #define I2C_CLASS_DDC          (1<<3)  /* DDC bus on graphics adapters */
-#define I2C_CLASS_SPD          (1<<7)  /* SPD EEPROMs and similar */
+#define I2C_CLASS_SPD          (1<<7)  /* Memory modules */
 
 /* Internal numbers to terminate lists */
 #define I2C_CLIENT_END         0xfffeU