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:
Linus Torvalds 2011-05-25 16:52:50 -07:00
commit 0c63e38a12
29 changed files with 1305 additions and 150 deletions

View 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

View 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

View 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.

View 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

View 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
-----------------

View 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)

View 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

View 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

View 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

View 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);
}

View 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

View 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

View 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)

View 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);

View 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);

539
drivers/hwmon/emc6w201.c Normal file
View 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");

View 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;

View 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)

View 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);

View 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) {

View 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,

View 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);

View 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);
}

View 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;

View File

@ -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]);
}

View 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

View 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);

View 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