mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 22:21:42 +00:00
Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging
* '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
This commit is contained in:
commit
0c63e38a12
42
Documentation/hwmon/emc6w201
Normal file
42
Documentation/hwmon/emc6w201
Normal file
@ -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
|
@ -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
|
||||
|
37
Documentation/hwmon/fam15h_power
Normal file
37
Documentation/hwmon/fam15h_power
Normal file
@ -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.
|
@ -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
|
||||
|
@ -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
|
||||
-----------------
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
539
drivers/hwmon/emc6w201.c
Normal file
539
drivers/hwmon/emc6w201.c
Normal file
@ -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");
|
@ -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;
|
||||
|
229
drivers/hwmon/fam15h_power.c
Normal file
229
drivers/hwmon/fam15h_power.c
Normal file
@ -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)
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -40,13 +40,6 @@
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#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]);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user