提交 36f021b5 编写于 作者: L Linus Torvalds

Merge branch 'hwmon-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6

* 'hwmon-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6: (32 commits)
  Use menuconfig objects - hwmon
  hwmon/smsc47b397: Use dynamic sysfs callbacks
  hwmon/smsc47b397: Convert to a platform driver
  hwmon/w83781d: Deprecate W83627HF support
  hwmon/w83781d: Use dynamic sysfs callbacks
  hwmon/w83781d: Be less i2c_client-centric
  hwmon/w83781d: Clean up conversion macros
  hwmon/w83781d: No longer use i2c-isa
  hwmon/ams: Do not print error on systems without apple motion sensor
  hwmon/ams: Fix I2C read retry logic
  hwmon: New AD7416, AD7417 and AD7418 driver
  hwmon/coretemp: Add documentation
  hwmon: New coretemp driver
  i386: Use functions from library in msr driver
  i386: Add safe variants of rdmsr_on_cpu and wrmsr_on_cpu
  hwmon/lm75: Use dynamic sysfs callbacks
  hwmon/lm78: Use dynamic sysfs callbacks
  hwmon/lm78: Be less i2c_client-centric
  hwmon/lm78: No longer use i2c-isa
  hwmon: New max6650 driver
  ...
Kernel driver coretemp
======================
Supported chips:
* All Intel Core family
Prefix: 'coretemp'
CPUID: family 0x6, models 0xe, 0xf
Datasheet: Intel 64 and IA-32 Architectures Software Developer's Manual
Volume 3A: System Programming Guide
Author: Rudolf Marek
Description
-----------
This driver permits reading temperature sensor embedded inside Intel Core CPU.
Temperature is measured in degrees Celsius and measurement resolution is
1 degree C. Valid temperatures are from 0 to TjMax degrees C, because
the actual value of temperature register is in fact a delta from TjMax.
Temperature known as TjMax is the maximum junction temperature of processor.
Intel defines this temperature as 85C or 100C. At this temperature, protection
mechanism will perform actions to forcibly cool down the processor. Alarm
may be raised, if the temperature grows enough (more than TjMax) to trigger
the Out-Of-Spec bit. Following table summarizes the exported sysfs files:
temp1_input - Core temperature (in millidegrees Celsius).
temp1_crit - Maximum junction temperature (in millidegrees Celsius).
temp1_crit_alarm - Set when Out-of-spec bit is set, never clears.
Correct CPU operation is no longer guaranteed.
temp1_label - Contains string "Core X", where X is processor
number.
The TjMax temperature is set to 85 degrees C if undocumented model specific
register (UMSR) 0xee has bit 30 set. If not the TjMax is 100 degrees C as
(sometimes) documented in processor datasheet.
Kernel driver max6650
=====================
Supported chips:
* Maxim 6650 / 6651
Prefix: 'max6650'
Addresses scanned: I2C 0x1b, 0x1f, 0x48, 0x4b
Datasheet: http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf
Authors:
Hans J. Koch <hjk@linutronix.de>
John Morris <john.morris@spirentcom.com>
Claus Gindhart <claus.gindhart@kontron.com>
Description
-----------
This driver implements support for the Maxim 6650/6651
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 driver is not able to distinguish between the 2 devices.
The driver provides the following sensor accesses in sysfs:
fan1_input ro fan tachometer speed in RPM
fan2_input ro "
fan3_input ro "
fan4_input ro "
fan1_target rw desired fan speed in RPM (closed loop mode only)
pwm1_enable rw regulator mode, 0=full on, 1=open loop, 2=closed loop
pwm1 rw relative speed (0-255), 255=max. speed.
Used in open loop mode only.
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.
Module parameters
-----------------
If your board has a BIOS that initializes the MAX6650/6651 correctly, you can
simply load your module without parameters. It won't touch the configuration
registers then. If your board BIOS doesn't initialize the chip, or you want
different settings, you can set the following parameters:
voltage_12V: 5=5V fan, 12=12V fan, 0=don't change
prescaler: Possible values are 1,2,4,8,16, or 0 for don't change
clock: The clock frequency in Hz of the chip the driver should assume [254000]
Please have a look at the MAX6650/6651 data sheet and make sure that you fully
understand the meaning of these parameters before you attempt to change them.
...@@ -14,6 +14,10 @@ Supported chips: ...@@ -14,6 +14,10 @@ Supported chips:
http://www.smsc.com/main/datasheets/47m14x.pdf http://www.smsc.com/main/datasheets/47m14x.pdf
http://www.smsc.com/main/tools/discontinued/47m15x.pdf http://www.smsc.com/main/tools/discontinued/47m15x.pdf
http://www.smsc.com/main/datasheets/47m192.pdf http://www.smsc.com/main/datasheets/47m192.pdf
* SMSC LPC47M292
Addresses scanned: none, address read from Super I/O config space
Prefix: 'smsc47m2'
Datasheet: Not public
* SMSC LPC47M997 * SMSC LPC47M997
Addresses scanned: none, address read from Super I/O config space Addresses scanned: none, address read from Super I/O config space
Prefix: 'smsc47m1' Prefix: 'smsc47m1'
...@@ -32,9 +36,10 @@ Description ...@@ -32,9 +36,10 @@ Description
The Standard Microsystems Corporation (SMSC) 47M1xx Super I/O chips The Standard Microsystems Corporation (SMSC) 47M1xx Super I/O chips
contain monitoring and PWM control circuitry for two fans. contain monitoring and PWM control circuitry for two fans.
The 47M15x and 47M192 chips contain a full 'hardware monitoring block' The LPC47M15x, LPC47M192 and LPC47M292 chips contain a full 'hardware
in addition to the fan monitoring and control. The hardware monitoring monitoring block' in addition to the fan monitoring and control. The
block is not supported by the driver. hardware monitoring block is not supported by this driver, use the
smsc47m192 driver for that.
No documentation is available for the 47M997, but it has the same device No documentation is available for the 47M997, but it has the same device
ID as the 47M15x and 47M192 chips and seems to be compatible. ID as the 47M15x and 47M192 chips and seems to be compatible.
......
...@@ -2,12 +2,13 @@ Kernel driver smsc47m192 ...@@ -2,12 +2,13 @@ Kernel driver smsc47m192
======================== ========================
Supported chips: Supported chips:
* SMSC LPC47M192 and LPC47M997 * SMSC LPC47M192, LPC47M15x, LPC47M292 and LPC47M997
Prefix: 'smsc47m192' Prefix: 'smsc47m192'
Addresses scanned: I2C 0x2c - 0x2d Addresses scanned: I2C 0x2c - 0x2d
Datasheet: The datasheet for LPC47M192 is publicly available from Datasheet: The datasheet for LPC47M192 is publicly available from
http://www.smsc.com/ http://www.smsc.com/
The LPC47M997 is compatible for hardware monitoring. The LPC47M15x, LPC47M292 and LPC47M997 are compatible for
hardware monitoring.
Author: Hartmut Rick <linux@rick.claranet.de> Author: Hartmut Rick <linux@rick.claranet.de>
Special thanks to Jean Delvare for careful checking Special thanks to Jean Delvare for careful checking
...@@ -18,7 +19,7 @@ Description ...@@ -18,7 +19,7 @@ Description
----------- -----------
This driver implements support for the hardware sensor capabilities This driver implements support for the hardware sensor capabilities
of the SMSC LPC47M192 and LPC47M997 Super-I/O chips. of the SMSC LPC47M192 and compatible Super-I/O chips.
These chips support 3 temperature channels and 8 voltage inputs These chips support 3 temperature channels and 8 voltage inputs
as well as CPU voltage VID input. as well as CPU voltage VID input.
......
...@@ -152,6 +152,13 @@ fan[1-*]_div Fan divisor. ...@@ -152,6 +152,13 @@ fan[1-*]_div Fan divisor.
Note that this is actually an internal clock divisor, which Note that this is actually an internal clock divisor, which
affects the measurable speed range, not the read value. affects the measurable speed range, not the read value.
fan[1-*]_target
Desired fan speed
Unit: revolution/min (RPM)
RW
Only makes sense if the chip supports closed-loop fan speed
control based on the measured fan speed.
Also see the Alarms section for status flags associated with fans. Also see the Alarms section for status flags associated with fans.
......
...@@ -1040,6 +1040,12 @@ P: Simon Arlott ...@@ -1040,6 +1040,12 @@ P: Simon Arlott
M: cxacru@fire.lp0.eu M: cxacru@fire.lp0.eu
S: Maintained S: Maintained
CORETEMP HARDWARE MONITORING DRIVER
P: Rudolf Marek
M: r.marek@assembler.cz
L: lm-sensors@lm-sensors.org
S: Maintained
COSA/SRP SYNC SERIAL DRIVER COSA/SRP SYNC SERIAL DRIVER
P: Jan "Yenya" Kasprzak P: Jan "Yenya" Kasprzak
M: kas@fi.muni.cz M: kas@fi.muni.cz
...@@ -2314,6 +2320,12 @@ M: vandrove@vc.cvut.cz ...@@ -2314,6 +2320,12 @@ M: vandrove@vc.cvut.cz
L: linux-fbdev-devel@lists.sourceforge.net (subscribers-only) L: linux-fbdev-devel@lists.sourceforge.net (subscribers-only)
S: Maintained S: Maintained
MAX6650 HARDWARE MONITOR AND FAN CONTROLLER DRIVER
P: Hans J. Koch
M: hjk@linutronix.de
L: lm-sensors@lm-sensors.org
S: Maintained
MEGARAID SCSI DRIVERS MEGARAID SCSI DRIVERS
P: Neela Syam Kolli P: Neela Syam Kolli
M: Neela.Kolli@engenio.com M: Neela.Kolli@engenio.com
......
...@@ -45,104 +45,6 @@ ...@@ -45,104 +45,6 @@
static struct class *msr_class; static struct class *msr_class;
static inline int wrmsr_eio(u32 reg, u32 eax, u32 edx)
{
int err;
err = wrmsr_safe(reg, eax, edx);
if (err)
err = -EIO;
return err;
}
static inline int rdmsr_eio(u32 reg, u32 *eax, u32 *edx)
{
int err;
err = rdmsr_safe(reg, eax, edx);
if (err)
err = -EIO;
return err;
}
#ifdef CONFIG_SMP
struct msr_command {
int err;
u32 reg;
u32 data[2];
};
static void msr_smp_wrmsr(void *cmd_block)
{
struct msr_command *cmd = (struct msr_command *)cmd_block;
cmd->err = wrmsr_eio(cmd->reg, cmd->data[0], cmd->data[1]);
}
static void msr_smp_rdmsr(void *cmd_block)
{
struct msr_command *cmd = (struct msr_command *)cmd_block;
cmd->err = rdmsr_eio(cmd->reg, &cmd->data[0], &cmd->data[1]);
}
static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx)
{
struct msr_command cmd;
int ret;
preempt_disable();
if (cpu == smp_processor_id()) {
ret = wrmsr_eio(reg, eax, edx);
} else {
cmd.reg = reg;
cmd.data[0] = eax;
cmd.data[1] = edx;
smp_call_function_single(cpu, msr_smp_wrmsr, &cmd, 1, 1);
ret = cmd.err;
}
preempt_enable();
return ret;
}
static inline int do_rdmsr(int cpu, u32 reg, u32 * eax, u32 * edx)
{
struct msr_command cmd;
int ret;
preempt_disable();
if (cpu == smp_processor_id()) {
ret = rdmsr_eio(reg, eax, edx);
} else {
cmd.reg = reg;
smp_call_function_single(cpu, msr_smp_rdmsr, &cmd, 1, 1);
*eax = cmd.data[0];
*edx = cmd.data[1];
ret = cmd.err;
}
preempt_enable();
return ret;
}
#else /* ! CONFIG_SMP */
static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx)
{
return wrmsr_eio(reg, eax, edx);
}
static inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx)
{
return rdmsr_eio(reg, eax, edx);
}
#endif /* ! CONFIG_SMP */
static loff_t msr_seek(struct file *file, loff_t offset, int orig) static loff_t msr_seek(struct file *file, loff_t offset, int orig)
{ {
loff_t ret = -EINVAL; loff_t ret = -EINVAL;
...@@ -174,9 +76,9 @@ static ssize_t msr_read(struct file *file, char __user * buf, ...@@ -174,9 +76,9 @@ static ssize_t msr_read(struct file *file, char __user * buf,
return -EINVAL; /* Invalid chunk size */ return -EINVAL; /* Invalid chunk size */
for (; count; count -= 8) { for (; count; count -= 8) {
err = do_rdmsr(cpu, reg, &data[0], &data[1]); err = rdmsr_safe_on_cpu(cpu, reg, &data[0], &data[1]);
if (err) if (err)
return err; return -EIO;
if (copy_to_user(tmp, &data, 8)) if (copy_to_user(tmp, &data, 8))
return -EFAULT; return -EFAULT;
tmp += 2; tmp += 2;
...@@ -200,9 +102,9 @@ static ssize_t msr_write(struct file *file, const char __user *buf, ...@@ -200,9 +102,9 @@ static ssize_t msr_write(struct file *file, const char __user *buf,
for (; count; count -= 8) { for (; count; count -= 8) {
if (copy_from_user(&data, tmp, 8)) if (copy_from_user(&data, tmp, 8))
return -EFAULT; return -EFAULT;
err = do_wrmsr(cpu, reg, data[0], data[1]); err = wrmsr_safe_on_cpu(cpu, reg, data[0], data[1]);
if (err) if (err)
return err; return -EIO;
tmp += 2; tmp += 2;
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
struct msr_info { struct msr_info {
u32 msr_no; u32 msr_no;
u32 l, h; u32 l, h;
int err;
}; };
static void __rdmsr_on_cpu(void *info) static void __rdmsr_on_cpu(void *info)
...@@ -15,20 +16,38 @@ static void __rdmsr_on_cpu(void *info) ...@@ -15,20 +16,38 @@ static void __rdmsr_on_cpu(void *info)
rdmsr(rv->msr_no, rv->l, rv->h); rdmsr(rv->msr_no, rv->l, rv->h);
} }
void rdmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 *l, u32 *h) static void __rdmsr_safe_on_cpu(void *info)
{ {
struct msr_info *rv = info;
rv->err = rdmsr_safe(rv->msr_no, &rv->l, &rv->h);
}
static int _rdmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 *l, u32 *h, int safe)
{
int err = 0;
preempt_disable(); preempt_disable();
if (smp_processor_id() == cpu) if (smp_processor_id() == cpu)
rdmsr(msr_no, *l, *h); if (safe)
err = rdmsr_safe(msr_no, l, h);
else
rdmsr(msr_no, *l, *h);
else { else {
struct msr_info rv; struct msr_info rv;
rv.msr_no = msr_no; rv.msr_no = msr_no;
smp_call_function_single(cpu, __rdmsr_on_cpu, &rv, 0, 1); if (safe) {
smp_call_function_single(cpu, __rdmsr_safe_on_cpu,
&rv, 0, 1);
err = rv.err;
} else {
smp_call_function_single(cpu, __rdmsr_on_cpu, &rv, 0, 1);
}
*l = rv.l; *l = rv.l;
*h = rv.h; *h = rv.h;
} }
preempt_enable(); preempt_enable();
return err;
} }
static void __wrmsr_on_cpu(void *info) static void __wrmsr_on_cpu(void *info)
...@@ -38,21 +57,63 @@ static void __wrmsr_on_cpu(void *info) ...@@ -38,21 +57,63 @@ static void __wrmsr_on_cpu(void *info)
wrmsr(rv->msr_no, rv->l, rv->h); wrmsr(rv->msr_no, rv->l, rv->h);
} }
void wrmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 l, u32 h) static void __wrmsr_safe_on_cpu(void *info)
{ {
struct msr_info *rv = info;
rv->err = wrmsr_safe(rv->msr_no, rv->l, rv->h);
}
static int _wrmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 l, u32 h, int safe)
{
int err = 0;
preempt_disable(); preempt_disable();
if (smp_processor_id() == cpu) if (smp_processor_id() == cpu)
wrmsr(msr_no, l, h); if (safe)
err = wrmsr_safe(msr_no, l, h);
else
wrmsr(msr_no, l, h);
else { else {
struct msr_info rv; struct msr_info rv;
rv.msr_no = msr_no; rv.msr_no = msr_no;
rv.l = l; rv.l = l;
rv.h = h; rv.h = h;
smp_call_function_single(cpu, __wrmsr_on_cpu, &rv, 0, 1); if (safe) {
smp_call_function_single(cpu, __wrmsr_safe_on_cpu,
&rv, 0, 1);
err = rv.err;
} else {
smp_call_function_single(cpu, __wrmsr_on_cpu, &rv, 0, 1);
}
} }
preempt_enable(); preempt_enable();
return err;
}
void wrmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 l, u32 h)
{
_wrmsr_on_cpu(cpu, msr_no, l, h, 0);
}
void rdmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 *l, u32 *h)
{
_rdmsr_on_cpu(cpu, msr_no, l, h, 0);
}
/* These "safe" variants are slower and should be used when the target MSR
may not actually exist. */
int wrmsr_safe_on_cpu(unsigned int cpu, u32 msr_no, u32 l, u32 h)
{
return _wrmsr_on_cpu(cpu, msr_no, l, h, 1);
}
int rdmsr_safe_on_cpu(unsigned int cpu, u32 msr_no, u32 *l, u32 *h)
{
return _rdmsr_on_cpu(cpu, msr_no, l, h, 1);
} }
EXPORT_SYMBOL(rdmsr_on_cpu); EXPORT_SYMBOL(rdmsr_on_cpu);
EXPORT_SYMBOL(wrmsr_on_cpu); EXPORT_SYMBOL(wrmsr_on_cpu);
EXPORT_SYMBOL(rdmsr_safe_on_cpu);
EXPORT_SYMBOL(wrmsr_safe_on_cpu);
...@@ -2,9 +2,7 @@ ...@@ -2,9 +2,7 @@
# Hardware monitoring chip drivers configuration # Hardware monitoring chip drivers configuration
# #
menu "Hardware Monitoring support" menuconfig HWMON
config HWMON
tristate "Hardware Monitoring support" tristate "Hardware Monitoring support"
default y default y
help help
...@@ -23,13 +21,15 @@ config HWMON ...@@ -23,13 +21,15 @@ config HWMON
This support can also be built as a module. If so, the module This support can also be built as a module. If so, the module
will be called hwmon. will be called hwmon.
if HWMON
config HWMON_VID config HWMON_VID
tristate tristate
default n default n
config SENSORS_ABITUGURU config SENSORS_ABITUGURU
tristate "Abit uGuru" tristate "Abit uGuru"
depends on HWMON && EXPERIMENTAL depends on EXPERIMENTAL
help help
If you say yes here you get support for the Abit uGuru chips If you say yes here you get support for the Abit uGuru chips
sensor part. The voltage and frequency control parts of the Abit sensor part. The voltage and frequency control parts of the Abit
...@@ -39,9 +39,19 @@ config SENSORS_ABITUGURU ...@@ -39,9 +39,19 @@ config SENSORS_ABITUGURU
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called abituguru. will be called abituguru.
config SENSORS_AD7418
tristate "Analog Devices AD7416, AD7417 and AD7418"
depends on I2C && EXPERIMENTAL
help
If you say yes here you get support for the Analog Devices
AD7416, AD7417 and AD7418 temperature monitoring chips.
This driver can also be built as a module. If so, the module
will be called ad7418.
config SENSORS_ADM1021 config SENSORS_ADM1021
tristate "Analog Devices ADM1021 and compatibles" tristate "Analog Devices ADM1021 and compatibles"
depends on HWMON && I2C depends on I2C
help help
If you say yes here you get support for Analog Devices ADM1021 If you say yes here you get support for Analog Devices ADM1021
and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A, and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A,
...@@ -53,7 +63,7 @@ config SENSORS_ADM1021 ...@@ -53,7 +63,7 @@ config SENSORS_ADM1021
config SENSORS_ADM1025 config SENSORS_ADM1025
tristate "Analog Devices ADM1025 and compatibles" tristate "Analog Devices ADM1025 and compatibles"
depends on HWMON && I2C depends on I2C
select HWMON_VID select HWMON_VID
help help
If you say yes here you get support for Analog Devices ADM1025 If you say yes here you get support for Analog Devices ADM1025
...@@ -64,7 +74,7 @@ config SENSORS_ADM1025 ...@@ -64,7 +74,7 @@ config SENSORS_ADM1025
config SENSORS_ADM1026 config SENSORS_ADM1026
tristate "Analog Devices ADM1026 and compatibles" tristate "Analog Devices ADM1026 and compatibles"
depends on HWMON && I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
select HWMON_VID select HWMON_VID
help help
If you say yes here you get support for Analog Devices ADM1026 If you say yes here you get support for Analog Devices ADM1026
...@@ -75,7 +85,7 @@ config SENSORS_ADM1026 ...@@ -75,7 +85,7 @@ config SENSORS_ADM1026
config SENSORS_ADM1029 config SENSORS_ADM1029
tristate "Analog Devices ADM1029" tristate "Analog Devices ADM1029"
depends on HWMON && I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
help help
If you say yes here you get support for Analog Devices ADM1029 If you say yes here you get support for Analog Devices ADM1029
sensor chip. sensor chip.
...@@ -86,7 +96,7 @@ config SENSORS_ADM1029 ...@@ -86,7 +96,7 @@ config SENSORS_ADM1029
config SENSORS_ADM1031 config SENSORS_ADM1031
tristate "Analog Devices ADM1031 and compatibles" tristate "Analog Devices ADM1031 and compatibles"
depends on HWMON && I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
help help
If you say yes here you get support for Analog Devices ADM1031 If you say yes here you get support for Analog Devices ADM1031
and ADM1030 sensor chips. and ADM1030 sensor chips.
...@@ -96,7 +106,7 @@ config SENSORS_ADM1031 ...@@ -96,7 +106,7 @@ config SENSORS_ADM1031
config SENSORS_ADM9240 config SENSORS_ADM9240
tristate "Analog Devices ADM9240 and compatibles" tristate "Analog Devices ADM9240 and compatibles"
depends on HWMON && I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
select HWMON_VID select HWMON_VID
help help
If you say yes here you get support for Analog Devices ADM9240, If you say yes here you get support for Analog Devices ADM9240,
...@@ -107,7 +117,7 @@ config SENSORS_ADM9240 ...@@ -107,7 +117,7 @@ config SENSORS_ADM9240
config SENSORS_K8TEMP config SENSORS_K8TEMP
tristate "AMD Athlon64/FX or Opteron temperature sensor" tristate "AMD Athlon64/FX or Opteron temperature sensor"
depends on HWMON && X86 && PCI && EXPERIMENTAL depends on X86 && PCI && EXPERIMENTAL
help help
If you say yes here you get support for the temperature If you say yes here you get support for the temperature
sensor(s) inside your CPU. Supported is whole AMD K8 sensor(s) inside your CPU. Supported is whole AMD K8
...@@ -119,7 +129,7 @@ config SENSORS_K8TEMP ...@@ -119,7 +129,7 @@ config SENSORS_K8TEMP
config SENSORS_AMS config SENSORS_AMS
tristate "Apple Motion Sensor driver" tristate "Apple Motion Sensor driver"
depends on HWMON && PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL depends on PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL
help help
Support for the motion sensor included in PowerBooks. Includes Support for the motion sensor included in PowerBooks. Includes
implementations for PMU and I2C. implementations for PMU and I2C.
...@@ -144,7 +154,7 @@ config SENSORS_AMS_I2C ...@@ -144,7 +154,7 @@ config SENSORS_AMS_I2C
config SENSORS_ASB100 config SENSORS_ASB100
tristate "Asus ASB100 Bach" tristate "Asus ASB100 Bach"
depends on HWMON && I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
select HWMON_VID select HWMON_VID
help help
If you say yes here you get support for the ASB100 Bach sensor If you say yes here you get support for the ASB100 Bach sensor
...@@ -155,7 +165,7 @@ config SENSORS_ASB100 ...@@ -155,7 +165,7 @@ config SENSORS_ASB100
config SENSORS_ATXP1 config SENSORS_ATXP1
tristate "Attansic ATXP1 VID controller" tristate "Attansic ATXP1 VID controller"
depends on HWMON && I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
select HWMON_VID select HWMON_VID
help help
If you say yes here you get support for the Attansic ATXP1 VID If you say yes here you get support for the Attansic ATXP1 VID
...@@ -169,7 +179,7 @@ config SENSORS_ATXP1 ...@@ -169,7 +179,7 @@ config SENSORS_ATXP1
config SENSORS_DS1621 config SENSORS_DS1621
tristate "Dallas Semiconductor DS1621 and DS1625" tristate "Dallas Semiconductor DS1621 and DS1625"
depends on HWMON && I2C depends on I2C
help help
If you say yes here you get support for Dallas Semiconductor If you say yes here you get support for Dallas Semiconductor
DS1621 and DS1625 sensor chips. DS1621 and DS1625 sensor chips.
...@@ -179,7 +189,7 @@ config SENSORS_DS1621 ...@@ -179,7 +189,7 @@ config SENSORS_DS1621
config SENSORS_F71805F config SENSORS_F71805F
tristate "Fintek F71805F/FG and F71872F/FG" tristate "Fintek F71805F/FG and F71872F/FG"
depends on HWMON && EXPERIMENTAL depends on EXPERIMENTAL
help help
If you say yes here you get support for hardware monitoring If you say yes here you get support for hardware monitoring
features of the Fintek F71805F/FG and F71872F/FG Super-I/O features of the Fintek F71805F/FG and F71872F/FG Super-I/O
...@@ -190,7 +200,7 @@ config SENSORS_F71805F ...@@ -190,7 +200,7 @@ config SENSORS_F71805F
config SENSORS_FSCHER config SENSORS_FSCHER
tristate "FSC Hermes" tristate "FSC Hermes"
depends on HWMON && I2C depends on I2C
help help
If you say yes here you get support for Fujitsu Siemens If you say yes here you get support for Fujitsu Siemens
Computers Hermes sensor chips. Computers Hermes sensor chips.
...@@ -200,7 +210,7 @@ config SENSORS_FSCHER ...@@ -200,7 +210,7 @@ config SENSORS_FSCHER
config SENSORS_FSCPOS config SENSORS_FSCPOS
tristate "FSC Poseidon" tristate "FSC Poseidon"
depends on HWMON && I2C depends on I2C
help help
If you say yes here you get support for Fujitsu Siemens If you say yes here you get support for Fujitsu Siemens
Computers Poseidon sensor chips. Computers Poseidon sensor chips.
...@@ -210,7 +220,7 @@ config SENSORS_FSCPOS ...@@ -210,7 +220,7 @@ config SENSORS_FSCPOS
config SENSORS_GL518SM config SENSORS_GL518SM
tristate "Genesys Logic GL518SM" tristate "Genesys Logic GL518SM"
depends on HWMON && I2C depends on I2C
help help
If you say yes here you get support for Genesys Logic GL518SM If you say yes here you get support for Genesys Logic GL518SM
sensor chips. sensor chips.
...@@ -220,7 +230,7 @@ config SENSORS_GL518SM ...@@ -220,7 +230,7 @@ config SENSORS_GL518SM
config SENSORS_GL520SM config SENSORS_GL520SM
tristate "Genesys Logic GL520SM" tristate "Genesys Logic GL520SM"
depends on HWMON && I2C depends on I2C
select HWMON_VID select HWMON_VID
help help
If you say yes here you get support for Genesys Logic GL520SM If you say yes here you get support for Genesys Logic GL520SM
...@@ -229,9 +239,17 @@ config SENSORS_GL520SM ...@@ -229,9 +239,17 @@ config SENSORS_GL520SM
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called gl520sm. will be called gl520sm.
config SENSORS_CORETEMP
tristate "Intel Core (2) Duo/Solo temperature sensor"
depends on X86 && EXPERIMENTAL
help
If you say yes here you get support for the temperature
sensor inside your CPU. Supported all are all known variants
of Intel Core family.
config SENSORS_IT87 config SENSORS_IT87
tristate "ITE IT87xx and compatibles" tristate "ITE IT87xx and compatibles"
depends on HWMON && I2C depends on I2C
select I2C_ISA select I2C_ISA
select HWMON_VID select HWMON_VID
help help
...@@ -243,7 +261,7 @@ config SENSORS_IT87 ...@@ -243,7 +261,7 @@ config SENSORS_IT87
config SENSORS_LM63 config SENSORS_LM63
tristate "National Semiconductor LM63" tristate "National Semiconductor LM63"
depends on HWMON && I2C depends on I2C
help help
If you say yes here you get support for the National Semiconductor If you say yes here you get support for the National Semiconductor
LM63 remote diode digital temperature sensor with integrated fan LM63 remote diode digital temperature sensor with integrated fan
...@@ -255,7 +273,7 @@ config SENSORS_LM63 ...@@ -255,7 +273,7 @@ config SENSORS_LM63
config SENSORS_LM70 config SENSORS_LM70
tristate "National Semiconductor LM70" tristate "National Semiconductor LM70"
depends on HWMON && SPI_MASTER && EXPERIMENTAL depends on SPI_MASTER && EXPERIMENTAL
help help
If you say yes here you get support for the National Semiconductor If you say yes here you get support for the National Semiconductor
LM70 digital temperature sensor chip. LM70 digital temperature sensor chip.
...@@ -265,7 +283,7 @@ config SENSORS_LM70 ...@@ -265,7 +283,7 @@ config SENSORS_LM70
config SENSORS_LM75 config SENSORS_LM75
tristate "National Semiconductor LM75 and compatibles" tristate "National Semiconductor LM75 and compatibles"
depends on HWMON && I2C depends on I2C
help help
If you say yes here you get support for National Semiconductor LM75 If you say yes here you get support for National Semiconductor LM75
sensor chips and clones: Dallas Semiconductor DS75 and DS1775 (in sensor chips and clones: Dallas Semiconductor DS75 and DS1775 (in
...@@ -280,7 +298,7 @@ config SENSORS_LM75 ...@@ -280,7 +298,7 @@ config SENSORS_LM75
config SENSORS_LM77 config SENSORS_LM77
tristate "National Semiconductor LM77" tristate "National Semiconductor LM77"
depends on HWMON && I2C depends on I2C
help help
If you say yes here you get support for National Semiconductor LM77 If you say yes here you get support for National Semiconductor LM77
sensor chips. sensor chips.
...@@ -290,8 +308,7 @@ config SENSORS_LM77 ...@@ -290,8 +308,7 @@ config SENSORS_LM77
config SENSORS_LM78 config SENSORS_LM78
tristate "National Semiconductor LM78 and compatibles" tristate "National Semiconductor LM78 and compatibles"
depends on HWMON && I2C depends on I2C
select I2C_ISA
select HWMON_VID select HWMON_VID
help help
If you say yes here you get support for National Semiconductor LM78, If you say yes here you get support for National Semiconductor LM78,
...@@ -302,7 +319,7 @@ config SENSORS_LM78 ...@@ -302,7 +319,7 @@ config SENSORS_LM78
config SENSORS_LM80 config SENSORS_LM80
tristate "National Semiconductor LM80" tristate "National Semiconductor LM80"
depends on HWMON && I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
help help
If you say yes here you get support for National Semiconductor If you say yes here you get support for National Semiconductor
LM80 sensor chips. LM80 sensor chips.
...@@ -312,7 +329,7 @@ config SENSORS_LM80 ...@@ -312,7 +329,7 @@ config SENSORS_LM80
config SENSORS_LM83 config SENSORS_LM83
tristate "National Semiconductor LM83 and compatibles" tristate "National Semiconductor LM83 and compatibles"
depends on HWMON && I2C depends on I2C
help help
If you say yes here you get support for National Semiconductor If you say yes here you get support for National Semiconductor
LM82 and LM83 sensor chips. LM82 and LM83 sensor chips.
...@@ -322,7 +339,7 @@ config SENSORS_LM83 ...@@ -322,7 +339,7 @@ config SENSORS_LM83
config SENSORS_LM85 config SENSORS_LM85
tristate "National Semiconductor LM85 and compatibles" tristate "National Semiconductor LM85 and compatibles"
depends on HWMON && I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
select HWMON_VID select HWMON_VID
help help
If you say yes here you get support for National Semiconductor LM85 If you say yes here you get support for National Semiconductor LM85
...@@ -333,7 +350,7 @@ config SENSORS_LM85 ...@@ -333,7 +350,7 @@ config SENSORS_LM85
config SENSORS_LM87 config SENSORS_LM87
tristate "National Semiconductor LM87" tristate "National Semiconductor LM87"
depends on HWMON && I2C depends on I2C
select HWMON_VID select HWMON_VID
help help
If you say yes here you get support for National Semiconductor LM87 If you say yes here you get support for National Semiconductor LM87
...@@ -344,7 +361,7 @@ config SENSORS_LM87 ...@@ -344,7 +361,7 @@ config SENSORS_LM87
config SENSORS_LM90 config SENSORS_LM90
tristate "National Semiconductor LM90 and compatibles" tristate "National Semiconductor LM90 and compatibles"
depends on HWMON && I2C depends on I2C
help help
If you say yes here you get support for National Semiconductor LM90, If you say yes here you get support for National Semiconductor LM90,
LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657 and LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657 and
...@@ -358,7 +375,7 @@ config SENSORS_LM90 ...@@ -358,7 +375,7 @@ config SENSORS_LM90
config SENSORS_LM92 config SENSORS_LM92
tristate "National Semiconductor LM92 and compatibles" tristate "National Semiconductor LM92 and compatibles"
depends on HWMON && I2C depends on I2C
help help
If you say yes here you get support for National Semiconductor LM92 If you say yes here you get support for National Semiconductor LM92
and Maxim MAX6635 sensor chips. and Maxim MAX6635 sensor chips.
...@@ -368,16 +385,26 @@ config SENSORS_LM92 ...@@ -368,16 +385,26 @@ config SENSORS_LM92
config SENSORS_MAX1619 config SENSORS_MAX1619
tristate "Maxim MAX1619 sensor chip" tristate "Maxim MAX1619 sensor chip"
depends on HWMON && I2C depends on I2C
help help
If you say yes here you get support for MAX1619 sensor chip. If you say yes here you get support for MAX1619 sensor chip.
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called max1619. will be called max1619.
config SENSORS_MAX6650
tristate "Maxim MAX6650 sensor chip"
depends on I2C && EXPERIMENTAL
help
If you say yes here you get support for the MAX6650 / MAX6651
sensor chips.
This driver can also be built as a module. If so, the module
will be called max6650.
config SENSORS_PC87360 config SENSORS_PC87360
tristate "National Semiconductor PC87360 family" tristate "National Semiconductor PC87360 family"
depends on HWMON && I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
select I2C_ISA select I2C_ISA
select HWMON_VID select HWMON_VID
help help
...@@ -392,7 +419,7 @@ config SENSORS_PC87360 ...@@ -392,7 +419,7 @@ config SENSORS_PC87360
config SENSORS_PC87427 config SENSORS_PC87427
tristate "National Semiconductor PC87427" tristate "National Semiconductor PC87427"
depends on HWMON && EXPERIMENTAL depends on EXPERIMENTAL
help help
If you say yes here you get access to the hardware monitoring If you say yes here you get access to the hardware monitoring
functions of the National Semiconductor PC87427 Super-I/O chip. functions of the National Semiconductor PC87427 Super-I/O chip.
...@@ -405,7 +432,7 @@ config SENSORS_PC87427 ...@@ -405,7 +432,7 @@ config SENSORS_PC87427
config SENSORS_SIS5595 config SENSORS_SIS5595
tristate "Silicon Integrated Systems Corp. SiS5595" tristate "Silicon Integrated Systems Corp. SiS5595"
depends on HWMON && I2C && PCI && EXPERIMENTAL depends on I2C && PCI && EXPERIMENTAL
select I2C_ISA select I2C_ISA
help help
If you say yes here you get support for the integrated sensors in If you say yes here you get support for the integrated sensors in
...@@ -416,28 +443,28 @@ config SENSORS_SIS5595 ...@@ -416,28 +443,28 @@ config SENSORS_SIS5595
config SENSORS_SMSC47M1 config SENSORS_SMSC47M1
tristate "SMSC LPC47M10x and compatibles" tristate "SMSC LPC47M10x and compatibles"
depends on HWMON && I2C
select I2C_ISA
help help
If you say yes here you get support for the integrated fan If you say yes here you get support for the integrated fan
monitoring and control capabilities of the SMSC LPC47B27x, monitoring and control capabilities of the SMSC LPC47B27x,
LPC47M10x, LPC47M112, LPC47M13x, LPC47M14x, LPC47M15x, LPC47M10x, LPC47M112, LPC47M13x, LPC47M14x, LPC47M15x,
LPC47M192 and LPC47M997 chips. LPC47M192, LPC47M292 and LPC47M997 chips.
The temperature and voltage sensor features of the LPC47M192 The temperature and voltage sensor features of the LPC47M15x,
and LPC47M997 are supported by another driver, select also LPC47M192, LPC47M292 and LPC47M997 are supported by another
"SMSC LPC47M192 and compatibles" below for those. driver, select also "SMSC LPC47M192 and compatibles" below for
those.
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called smsc47m1. will be called smsc47m1.
config SENSORS_SMSC47M192 config SENSORS_SMSC47M192
tristate "SMSC LPC47M192 and compatibles" tristate "SMSC LPC47M192 and compatibles"
depends on HWMON && I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
select HWMON_VID select HWMON_VID
help help
If you say yes here you get support for the temperature and If you say yes here you get support for the temperature and
voltage sensors of the SMSC LPC47M192 and LPC47M997 chips. voltage sensors of the SMSC LPC47M192, LPC47M15x, LPC47M292
and LPC47M997 chips.
The fan monitoring and control capabilities of these chips The fan monitoring and control capabilities of these chips
are supported by another driver, select are supported by another driver, select
...@@ -449,8 +476,7 @@ config SENSORS_SMSC47M192 ...@@ -449,8 +476,7 @@ config SENSORS_SMSC47M192
config SENSORS_SMSC47B397 config SENSORS_SMSC47B397
tristate "SMSC LPC47B397-NC" tristate "SMSC LPC47B397-NC"
depends on HWMON && I2C && EXPERIMENTAL depends on EXPERIMENTAL
select I2C_ISA
help help
If you say yes here you get support for the SMSC LPC47B397-NC If you say yes here you get support for the SMSC LPC47B397-NC
sensor chip. sensor chip.
...@@ -460,7 +486,7 @@ config SENSORS_SMSC47B397 ...@@ -460,7 +486,7 @@ config SENSORS_SMSC47B397
config SENSORS_VIA686A config SENSORS_VIA686A
tristate "VIA686A" tristate "VIA686A"
depends on HWMON && I2C && PCI depends on I2C && PCI
select I2C_ISA select I2C_ISA
help help
If you say yes here you get support for the integrated sensors in If you say yes here you get support for the integrated sensors in
...@@ -471,7 +497,7 @@ config SENSORS_VIA686A ...@@ -471,7 +497,7 @@ config SENSORS_VIA686A
config SENSORS_VT1211 config SENSORS_VT1211
tristate "VIA VT1211" tristate "VIA VT1211"
depends on HWMON && EXPERIMENTAL depends on EXPERIMENTAL
select HWMON_VID select HWMON_VID
help help
If you say yes here then you get support for hardware monitoring If you say yes here then you get support for hardware monitoring
...@@ -482,7 +508,7 @@ config SENSORS_VT1211 ...@@ -482,7 +508,7 @@ config SENSORS_VT1211
config SENSORS_VT8231 config SENSORS_VT8231
tristate "VIA VT8231" tristate "VIA VT8231"
depends on HWMON && I2C && PCI && EXPERIMENTAL depends on I2C && PCI && EXPERIMENTAL
select HWMON_VID select HWMON_VID
select I2C_ISA select I2C_ISA
help help
...@@ -494,8 +520,7 @@ config SENSORS_VT8231 ...@@ -494,8 +520,7 @@ config SENSORS_VT8231
config SENSORS_W83781D config SENSORS_W83781D
tristate "Winbond W83781D, W83782D, W83783S, W83627HF, Asus AS99127F" tristate "Winbond W83781D, W83782D, W83783S, W83627HF, Asus AS99127F"
depends on HWMON && I2C depends on I2C
select I2C_ISA
select HWMON_VID select HWMON_VID
help help
If you say yes here you get support for the Winbond W8378x series If you say yes here you get support for the Winbond W8378x series
...@@ -507,7 +532,7 @@ config SENSORS_W83781D ...@@ -507,7 +532,7 @@ config SENSORS_W83781D
config SENSORS_W83791D config SENSORS_W83791D
tristate "Winbond W83791D" tristate "Winbond W83791D"
depends on HWMON && I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
select HWMON_VID select HWMON_VID
help help
If you say yes here you get support for the Winbond W83791D chip. If you say yes here you get support for the Winbond W83791D chip.
...@@ -517,7 +542,7 @@ config SENSORS_W83791D ...@@ -517,7 +542,7 @@ config SENSORS_W83791D
config SENSORS_W83792D config SENSORS_W83792D
tristate "Winbond W83792D" tristate "Winbond W83792D"
depends on HWMON && I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
help help
If you say yes here you get support for the Winbond W83792D chip. If you say yes here you get support for the Winbond W83792D chip.
...@@ -526,7 +551,7 @@ config SENSORS_W83792D ...@@ -526,7 +551,7 @@ config SENSORS_W83792D
config SENSORS_W83793 config SENSORS_W83793
tristate "Winbond W83793" tristate "Winbond W83793"
depends on HWMON && I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
select HWMON_VID select HWMON_VID
help help
If you say yes here you get support for the Winbond W83793 If you say yes here you get support for the Winbond W83793
...@@ -537,7 +562,7 @@ config SENSORS_W83793 ...@@ -537,7 +562,7 @@ config SENSORS_W83793
config SENSORS_W83L785TS config SENSORS_W83L785TS
tristate "Winbond W83L785TS-S" tristate "Winbond W83L785TS-S"
depends on HWMON && I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
help help
If you say yes here you get support for the Winbond W83L785TS-S If you say yes here you get support for the Winbond W83L785TS-S
sensor chip, which is used on the Asus A7N8X, among other sensor chip, which is used on the Asus A7N8X, among other
...@@ -548,8 +573,6 @@ config SENSORS_W83L785TS ...@@ -548,8 +573,6 @@ config SENSORS_W83L785TS
config SENSORS_W83627HF config SENSORS_W83627HF
tristate "Winbond W83627HF, W83627THF, W83637HF, W83687THF, W83697HF" tristate "Winbond W83627HF, W83627THF, W83637HF, W83687THF, W83697HF"
depends on HWMON && I2C
select I2C_ISA
select HWMON_VID select HWMON_VID
help help
If you say yes here you get support for the Winbond W836X7 series If you say yes here you get support for the Winbond W836X7 series
...@@ -561,7 +584,7 @@ config SENSORS_W83627HF ...@@ -561,7 +584,7 @@ config SENSORS_W83627HF
config SENSORS_W83627EHF config SENSORS_W83627EHF
tristate "Winbond W83627EHF" tristate "Winbond W83627EHF"
depends on HWMON && I2C && EXPERIMENTAL depends on I2C && EXPERIMENTAL
select I2C_ISA select I2C_ISA
help help
If you say yes here you get preliminary support for the hardware If you say yes here you get preliminary support for the hardware
...@@ -577,7 +600,7 @@ config SENSORS_W83627EHF ...@@ -577,7 +600,7 @@ config SENSORS_W83627EHF
config SENSORS_HDAPS config SENSORS_HDAPS
tristate "IBM Hard Drive Active Protection System (hdaps)" tristate "IBM Hard Drive Active Protection System (hdaps)"
depends on HWMON && INPUT && X86 depends on INPUT && X86
default n default n
help help
This driver provides support for the IBM Hard Drive Active Protection This driver provides support for the IBM Hard Drive Active Protection
...@@ -620,7 +643,6 @@ config SENSORS_APPLESMC ...@@ -620,7 +643,6 @@ config SENSORS_APPLESMC
config HWMON_DEBUG_CHIP config HWMON_DEBUG_CHIP
bool "Hardware Monitoring Chip debugging messages" bool "Hardware Monitoring Chip debugging messages"
depends on HWMON
default n default n
help help
Say Y here if you want the I2C chip drivers to produce a bunch of Say Y here if you want the I2C chip drivers to produce a bunch of
...@@ -628,4 +650,4 @@ config HWMON_DEBUG_CHIP ...@@ -628,4 +650,4 @@ config HWMON_DEBUG_CHIP
a problem with I2C support and want to see more of what is going a problem with I2C support and want to see more of what is going
on. on.
endmenu endif # HWMON
...@@ -14,6 +14,7 @@ obj-$(CONFIG_SENSORS_W83781D) += w83781d.o ...@@ -14,6 +14,7 @@ obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
obj-$(CONFIG_SENSORS_W83791D) += w83791d.o obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
obj-$(CONFIG_SENSORS_AD7418) += ad7418.o
obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o
obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o
obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o
...@@ -23,6 +24,7 @@ obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o ...@@ -23,6 +24,7 @@ obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o
obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
obj-$(CONFIG_SENSORS_AMS) += ams/ obj-$(CONFIG_SENSORS_AMS) += ams/
obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
obj-$(CONFIG_SENSORS_F71805F) += f71805f.o obj-$(CONFIG_SENSORS_F71805F) += f71805f.o
obj-$(CONFIG_SENSORS_FSCHER) += fscher.o obj-$(CONFIG_SENSORS_FSCHER) += fscher.o
...@@ -44,6 +46,7 @@ obj-$(CONFIG_SENSORS_LM87) += lm87.o ...@@ -44,6 +46,7 @@ obj-$(CONFIG_SENSORS_LM87) += lm87.o
obj-$(CONFIG_SENSORS_LM90) += lm90.o obj-$(CONFIG_SENSORS_LM90) += lm90.o
obj-$(CONFIG_SENSORS_LM92) += lm92.o obj-$(CONFIG_SENSORS_LM92) += lm92.o
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o
......
/*
* An hwmon driver for the Analog Devices AD7416/17/18
* Copyright (C) 2006-07 Tower Technologies
*
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
* Based on lm75.c
* Copyright (C) 1998-99 Frodo Looijaard <frodol@dds.nl>
*
* 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 - version 2.
*/
#include <linux/module.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>
#include <linux/delay.h>
#include "lm75.h"
#define DRV_VERSION "0.3"
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x28, I2C_CLIENT_END };
/* Insmod parameters */
I2C_CLIENT_INSMOD_3(ad7416, ad7417, ad7418);
/* AD7418 registers */
#define AD7418_REG_TEMP_IN 0x00
#define AD7418_REG_CONF 0x01
#define AD7418_REG_TEMP_HYST 0x02
#define AD7418_REG_TEMP_OS 0x03
#define AD7418_REG_ADC 0x04
#define AD7418_REG_CONF2 0x05
#define AD7418_REG_ADC_CH(x) ((x) << 5)
#define AD7418_CH_TEMP AD7418_REG_ADC_CH(0)
static const u8 AD7418_REG_TEMP[] = { AD7418_REG_TEMP_IN,
AD7418_REG_TEMP_HYST,
AD7418_REG_TEMP_OS };
struct ad7418_data {
struct i2c_client client;
struct class_device *class_dev;
struct attribute_group attrs;
enum chips type;
struct mutex lock;
int adc_max; /* number of ADC channels */
char valid;
unsigned long last_updated; /* In jiffies */
s16 temp[3]; /* Register values */
u16 in[4];
};
static int ad7418_attach_adapter(struct i2c_adapter *adapter);
static int ad7418_detect(struct i2c_adapter *adapter, int address, int kind);
static int ad7418_detach_client(struct i2c_client *client);
static struct i2c_driver ad7418_driver = {
.driver = {
.name = "ad7418",
},
.attach_adapter = ad7418_attach_adapter,
.detach_client = ad7418_detach_client,
};
/* All registers are word-sized, except for the configuration registers.
* AD7418 uses a high-byte first convention. Do NOT use those functions to
* access the configuration registers CONF and CONF2, as they are byte-sized.
*/
static inline int ad7418_read(struct i2c_client *client, u8 reg)
{
return swab16(i2c_smbus_read_word_data(client, reg));
}
static inline int ad7418_write(struct i2c_client *client, u8 reg, u16 value)
{
return i2c_smbus_write_word_data(client, reg, swab16(value));
}
static void ad7418_init_client(struct i2c_client *client)
{
struct ad7418_data *data = i2c_get_clientdata(client);
int reg = i2c_smbus_read_byte_data(client, AD7418_REG_CONF);
if (reg < 0) {
dev_err(&client->dev, "cannot read configuration register\n");
} else {
dev_info(&client->dev, "configuring for mode 1\n");
i2c_smbus_write_byte_data(client, AD7418_REG_CONF, reg & 0xfe);
if (data->type == ad7417 || data->type == ad7418)
i2c_smbus_write_byte_data(client,
AD7418_REG_CONF2, 0x00);
}
}
static struct ad7418_data *ad7418_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct ad7418_data *data = i2c_get_clientdata(client);
mutex_lock(&data->lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
u8 cfg;
int i, ch;
/* read config register and clear channel bits */
cfg = i2c_smbus_read_byte_data(client, AD7418_REG_CONF);
cfg &= 0x1F;
i2c_smbus_write_byte_data(client, AD7418_REG_CONF,
cfg | AD7418_CH_TEMP);
udelay(30);
for (i = 0; i < 3; i++) {
data->temp[i] = ad7418_read(client, AD7418_REG_TEMP[i]);
}
for (i = 0, ch = 4; i < data->adc_max; i++, ch--) {
i2c_smbus_write_byte_data(client,
AD7418_REG_CONF,
cfg | AD7418_REG_ADC_CH(ch));
udelay(15);
data->in[data->adc_max - 1 - i] =
ad7418_read(client, AD7418_REG_ADC);
}
/* restore old configuration value */
ad7418_write(client, AD7418_REG_CONF, cfg);
data->last_updated = jiffies;
data->valid = 1;
}
mutex_unlock(&data->lock);
return data;
}
static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct ad7418_data *data = ad7418_update_device(dev);
return sprintf(buf, "%d\n",
LM75_TEMP_FROM_REG(data->temp[attr->index]));
}
static ssize_t show_adc(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct ad7418_data *data = ad7418_update_device(dev);
return sprintf(buf, "%d\n",
((data->in[attr->index] >> 6) * 2500 + 512) / 1024);
}
static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct i2c_client *client = to_i2c_client(dev);
struct ad7418_data *data = i2c_get_clientdata(client);
int temp = simple_strtol(buf, NULL, 10);
mutex_lock(&data->lock);
data->temp[attr->index] = LM75_TEMP_TO_REG(temp);
ad7418_write(client, AD7418_REG_TEMP[attr->index], data->temp[attr->index]);
mutex_unlock(&data->lock);
return count;
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
show_temp, set_temp, 1);
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
show_temp, set_temp, 2);
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_adc, NULL, 0);
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_adc, NULL, 1);
static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_adc, NULL, 2);
static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_adc, NULL, 3);
static int ad7418_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_probe(adapter, &addr_data, ad7418_detect);
}
static struct attribute *ad7416_attributes[] = {
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr,
NULL
};
static struct attribute *ad7417_attributes[] = {
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr,
&sensor_dev_attr_in3_input.dev_attr.attr,
&sensor_dev_attr_in4_input.dev_attr.attr,
NULL
};
static struct attribute *ad7418_attributes[] = {
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_in1_input.dev_attr.attr,
NULL
};
static int ad7418_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *client;
struct ad7418_data *data;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
goto exit;
if (!(data = kzalloc(sizeof(struct ad7418_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
client = &data->client;
client->addr = address;
client->adapter = adapter;
client->driver = &ad7418_driver;
i2c_set_clientdata(client, data);
mutex_init(&data->lock);
/* AD7418 has a curious behaviour on registers 6 and 7. They
* both always read 0xC071 and are not documented on the datasheet.
* We use them to detect the chip.
*/
if (kind <= 0) {
int reg, reg6, reg7;
/* the AD7416 lies within this address range, but I have
* no means to check.
*/
if (address >= 0x48 && address <= 0x4f) {
/* XXX add tests for AD7416 here */
/* data->type = ad7416; */
}
/* here we might have AD7417 or AD7418 */
else if (address >= 0x28 && address <= 0x2f) {
reg6 = i2c_smbus_read_word_data(client, 0x06);
reg7 = i2c_smbus_read_word_data(client, 0x07);
if (address == 0x28 && reg6 == 0xC071 && reg7 == 0xC071)
data->type = ad7418;
/* XXX add tests for AD7417 here */
/* both AD7417 and AD7418 have bits 0-5 of
* the CONF2 register at 0
*/
reg = i2c_smbus_read_byte_data(client,
AD7418_REG_CONF2);
if (reg & 0x3F)
data->type = any_chip; /* detection failed */
}
} else {
dev_dbg(&adapter->dev, "detection forced\n");
}
if (kind > 0)
data->type = kind;
else if (kind < 0 && data->type == any_chip) {
err = -ENODEV;
goto exit_free;
}
switch (data->type) {
case any_chip:
case ad7416:
data->adc_max = 0;
data->attrs.attrs = ad7416_attributes;
strlcpy(client->name, "ad7416", I2C_NAME_SIZE);
break;
case ad7417:
data->adc_max = 4;
data->attrs.attrs = ad7417_attributes;
strlcpy(client->name, "ad7417", I2C_NAME_SIZE);
break;
case ad7418:
data->adc_max = 1;
data->attrs.attrs = ad7418_attributes;
strlcpy(client->name, "ad7418", I2C_NAME_SIZE);
break;
}
if ((err = i2c_attach_client(client)))
goto exit_free;
dev_info(&client->dev, "%s chip found\n", client->name);
/* Initialize the AD7418 chip */
ad7418_init_client(client);
/* Register sysfs hooks */
if ((err = sysfs_create_group(&client->dev.kobj, &data->attrs)))
goto exit_detach;
data->class_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
goto exit_remove;
}
return 0;
exit_remove:
sysfs_remove_group(&client->dev.kobj, &data->attrs);
exit_detach:
i2c_detach_client(client);
exit_free:
kfree(data);
exit:
return err;
}
static int ad7418_detach_client(struct i2c_client *client)
{
struct ad7418_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->class_dev);
sysfs_remove_group(&client->dev.kobj, &data->attrs);
i2c_detach_client(client);
kfree(data);
return 0;
}
static int __init ad7418_init(void)
{
return i2c_add_driver(&ad7418_driver);
}
static void __exit ad7418_exit(void)
{
i2c_del_driver(&ad7418_driver);
}
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("AD7416/17/18 driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
module_init(ad7418_init);
module_exit(ad7418_exit);
...@@ -219,9 +219,6 @@ int __init ams_init(void) ...@@ -219,9 +219,6 @@ int __init ams_init(void)
/* Found PMU motion sensor */ /* Found PMU motion sensor */
return ams_pmu_init(np); return ams_pmu_init(np);
#endif #endif
printk(KERN_ERR "ams: No motion sensor found.\n");
return -ENODEV; return -ENODEV;
} }
......
...@@ -85,17 +85,17 @@ static int ams_i2c_write(u8 reg, u8 value) ...@@ -85,17 +85,17 @@ static int ams_i2c_write(u8 reg, u8 value)
static int ams_i2c_cmd(enum ams_i2c_cmd cmd) static int ams_i2c_cmd(enum ams_i2c_cmd cmd)
{ {
s32 result; s32 result;
int remaining = HZ / 20; int count = 3;
ams_i2c_write(AMS_COMMAND, cmd); ams_i2c_write(AMS_COMMAND, cmd);
mdelay(5); msleep(5);
while (remaining) { while (count--) {
result = ams_i2c_read(AMS_COMMAND); result = ams_i2c_read(AMS_COMMAND);
if (result == 0 || result & 0x80) if (result == 0 || result & 0x80)
return 0; return 0;
remaining = schedule_timeout(remaining); schedule_timeout_uninterruptible(HZ / 20);
} }
return -1; return -1;
......
/*
* coretemp.c - Linux kernel module for hardware monitoring
*
* Copyright (C) 2007 Rudolf Marek <r.marek@assembler.cz>
*
* Inspired from many hwmon drivers
*
* 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; version 2 of the License.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/hwmon.h>
#include <linux/sysfs.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/cpu.h>
#include <asm/msr.h>
#include <asm/processor.h>
#define DRVNAME "coretemp"
typedef enum { SHOW_TEMP, SHOW_TJMAX, SHOW_LABEL, SHOW_NAME } SHOW;
/*
* Functions declaration
*/
static struct coretemp_data *coretemp_update_device(struct device *dev);
struct coretemp_data {
struct class_device *class_dev;
struct mutex update_lock;
const char *name;
u32 id;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
int temp;
int tjmax;
u8 alarm;
};
static struct coretemp_data *coretemp_update_device(struct device *dev);
/*
* Sysfs stuff
*/
static ssize_t show_name(struct device *dev, struct device_attribute
*devattr, char *buf)
{
int ret;
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct coretemp_data *data = dev_get_drvdata(dev);
if (attr->index == SHOW_NAME)
ret = sprintf(buf, "%s\n", data->name);
else /* show label */
ret = sprintf(buf, "Core %d\n", data->id);
return ret;
}
static ssize_t show_alarm(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct coretemp_data *data = coretemp_update_device(dev);
/* read the Out-of-spec log, never clear */
return sprintf(buf, "%d\n", data->alarm);
}
static ssize_t show_temp(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct coretemp_data *data = coretemp_update_device(dev);
int err;
if (attr->index == SHOW_TEMP)
err = data->valid ? sprintf(buf, "%d\n", data->temp) : -EAGAIN;
else
err = sprintf(buf, "%d\n", data->tjmax);
return err;
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL,
SHOW_TEMP);
static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, NULL,
SHOW_TJMAX);
static DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL);
static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL);
static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, SHOW_NAME);
static struct attribute *coretemp_attributes[] = {
&sensor_dev_attr_name.dev_attr.attr,
&sensor_dev_attr_temp1_label.dev_attr.attr,
&dev_attr_temp1_crit_alarm.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_crit.dev_attr.attr,
NULL
};
static const struct attribute_group coretemp_group = {
.attrs = coretemp_attributes,
};
static struct coretemp_data *coretemp_update_device(struct device *dev)
{
struct coretemp_data *data = dev_get_drvdata(dev);
mutex_lock(&data->update_lock);
if (!data->valid || time_after(jiffies, data->last_updated + HZ)) {
u32 eax, edx;
data->valid = 0;
rdmsr_on_cpu(data->id, MSR_IA32_THERM_STATUS, &eax, &edx);
data->alarm = (eax >> 5) & 1;
/* update only if data has been valid */
if (eax & 0x80000000) {
data->temp = data->tjmax - (((eax >> 16)
& 0x7f) * 1000);
data->valid = 1;
} else {
dev_dbg(dev, "Temperature data invalid (0x%x)\n", eax);
}
data->last_updated = jiffies;
}
mutex_unlock(&data->update_lock);
return data;
}
static int __devinit coretemp_probe(struct platform_device *pdev)
{
struct coretemp_data *data;
struct cpuinfo_x86 *c = &(cpu_data)[pdev->id];
int err;
u32 eax, edx;
if (!(data = kzalloc(sizeof(struct coretemp_data), GFP_KERNEL))) {
err = -ENOMEM;
dev_err(&pdev->dev, "Out of memory\n");
goto exit;
}
data->id = pdev->id;
data->name = "coretemp";
mutex_init(&data->update_lock);
/* Tjmax default is 100 degrees C */
data->tjmax = 100000;
/* test if we can access the THERM_STATUS MSR */
err = rdmsr_safe_on_cpu(data->id, MSR_IA32_THERM_STATUS, &eax, &edx);
if (err) {
dev_err(&pdev->dev,
"Unable to access THERM_STATUS MSR, giving up\n");
goto exit_free;
}
/* Some processors have Tjmax 85 following magic should detect it
Intel won't disclose the information without signed NDA, but
individuals cannot sign it. Catch(ed) 22.
*/
if (((c->x86_model == 0xf) && (c->x86_mask > 3)) ||
(c->x86_model == 0xe)) {
err = rdmsr_safe_on_cpu(data->id, 0xee, &eax, &edx);
if (err) {
dev_warn(&pdev->dev,
"Unable to access MSR 0xEE, Tjmax left at %d "
"degrees C\n", data->tjmax/1000);
} else if (eax & 0x40000000) {
data->tjmax = 85000;
}
}
platform_set_drvdata(pdev, data);
if ((err = sysfs_create_group(&pdev->dev.kobj, &coretemp_group)))
goto exit_free;
data->class_dev = hwmon_device_register(&pdev->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
dev_err(&pdev->dev, "Class registration failed (%d)\n",
err);
goto exit_class;
}
return 0;
exit_class:
sysfs_remove_group(&pdev->dev.kobj, &coretemp_group);
exit_free:
kfree(data);
exit:
return err;
}
static int __devexit coretemp_remove(struct platform_device *pdev)
{
struct coretemp_data *data = platform_get_drvdata(pdev);
hwmon_device_unregister(data->class_dev);
sysfs_remove_group(&pdev->dev.kobj, &coretemp_group);
platform_set_drvdata(pdev, NULL);
kfree(data);
return 0;
}
static struct platform_driver coretemp_driver = {
.driver = {
.owner = THIS_MODULE,
.name = DRVNAME,
},
.probe = coretemp_probe,
.remove = __devexit_p(coretemp_remove),
};
struct pdev_entry {
struct list_head list;
struct platform_device *pdev;
unsigned int cpu;
};
static LIST_HEAD(pdev_list);
static DEFINE_MUTEX(pdev_list_mutex);
static int __cpuinit coretemp_device_add(unsigned int cpu)
{
int err;
struct platform_device *pdev;
struct pdev_entry *pdev_entry;
pdev = platform_device_alloc(DRVNAME, cpu);
if (!pdev) {
err = -ENOMEM;
printk(KERN_ERR DRVNAME ": Device allocation failed\n");
goto exit;
}
pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL);
if (!pdev_entry) {
err = -ENOMEM;
goto exit_device_put;
}
err = platform_device_add(pdev);
if (err) {
printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
err);
goto exit_device_free;
}
pdev_entry->pdev = pdev;
pdev_entry->cpu = cpu;
mutex_lock(&pdev_list_mutex);
list_add_tail(&pdev_entry->list, &pdev_list);
mutex_unlock(&pdev_list_mutex);
return 0;
exit_device_free:
kfree(pdev_entry);
exit_device_put:
platform_device_put(pdev);
exit:
return err;
}
#ifdef CONFIG_HOTPLUG_CPU
void coretemp_device_remove(unsigned int cpu)
{
struct pdev_entry *p, *n;
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
if (p->cpu == cpu) {
platform_device_unregister(p->pdev);
list_del(&p->list);
kfree(p);
}
}
mutex_unlock(&pdev_list_mutex);
}
static int coretemp_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
unsigned int cpu = (unsigned long) hcpu;
switch (action) {
case CPU_ONLINE:
coretemp_device_add(cpu);
break;
case CPU_DEAD:
coretemp_device_remove(cpu);
break;
}
return NOTIFY_OK;
}
static struct notifier_block __cpuinitdata coretemp_cpu_notifier = {
.notifier_call = coretemp_cpu_callback,
};
#endif /* !CONFIG_HOTPLUG_CPU */
static int __init coretemp_init(void)
{
int i, err = -ENODEV;
struct pdev_entry *p, *n;
printk(KERN_NOTICE DRVNAME ": This driver uses undocumented features "
"of Core CPU. Temperature might be wrong!\n");
/* quick check if we run Intel */
if (cpu_data[0].x86_vendor != X86_VENDOR_INTEL)
goto exit;
err = platform_driver_register(&coretemp_driver);
if (err)
goto exit;
for_each_online_cpu(i) {
struct cpuinfo_x86 *c = &(cpu_data)[i];
/* check if family 6, models e, f */
if ((c->cpuid_level < 0) || (c->x86 != 0x6) ||
!((c->x86_model == 0xe) || (c->x86_model == 0xf))) {
/* supported CPU not found, but report the unknown
family 6 CPU */
if ((c->x86 == 0x6) && (c->x86_model > 0xf))
printk(KERN_WARNING DRVNAME ": Unknown CPU "
"model %x\n", c->x86_model);
continue;
}
err = coretemp_device_add(i);
if (err)
goto exit_devices_unreg;
}
if (list_empty(&pdev_list)) {
err = -ENODEV;
goto exit_driver_unreg;
}
#ifdef CONFIG_HOTPLUG_CPU
register_hotcpu_notifier(&coretemp_cpu_notifier);
#endif
return 0;
exit_devices_unreg:
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
platform_device_unregister(p->pdev);
list_del(&p->list);
kfree(p);
}
mutex_unlock(&pdev_list_mutex);
exit_driver_unreg:
platform_driver_unregister(&coretemp_driver);
exit:
return err;
}
static void __exit coretemp_exit(void)
{
struct pdev_entry *p, *n;
#ifdef CONFIG_HOTPLUG_CPU
unregister_hotcpu_notifier(&coretemp_cpu_notifier);
#endif
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
platform_device_unregister(p->pdev);
list_del(&p->list);
kfree(p);
}
mutex_unlock(&pdev_list_mutex);
platform_driver_unregister(&coretemp_driver);
}
MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>");
MODULE_DESCRIPTION("Intel Core temperature monitor");
MODULE_LICENSE("GPL");
module_init(coretemp_init)
module_exit(coretemp_exit)
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/ioport.h>
#include <asm/io.h> #include <asm/io.h>
static struct platform_device *pdev; static struct platform_device *pdev;
...@@ -1140,6 +1141,13 @@ static int __devinit f71805f_probe(struct platform_device *pdev) ...@@ -1140,6 +1141,13 @@ static int __devinit f71805f_probe(struct platform_device *pdev)
} }
res = platform_get_resource(pdev, IORESOURCE_IO, 0); res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!request_region(res->start + ADDR_REG_OFFSET, 2, DRVNAME)) {
err = -EBUSY;
dev_err(&pdev->dev, "Failed to request region 0x%lx-0x%lx\n",
(unsigned long)(res->start + ADDR_REG_OFFSET),
(unsigned long)(res->start + ADDR_REG_OFFSET + 1));
goto exit_free;
}
data->addr = res->start; data->addr = res->start;
data->name = names[sio_data->kind]; data->name = names[sio_data->kind];
mutex_init(&data->update_lock); mutex_init(&data->update_lock);
...@@ -1165,7 +1173,7 @@ static int __devinit f71805f_probe(struct platform_device *pdev) ...@@ -1165,7 +1173,7 @@ static int __devinit f71805f_probe(struct platform_device *pdev)
/* Register sysfs interface files */ /* Register sysfs interface files */
if ((err = sysfs_create_group(&pdev->dev.kobj, &f71805f_group))) if ((err = sysfs_create_group(&pdev->dev.kobj, &f71805f_group)))
goto exit_free; goto exit_release_region;
if (data->has_in & (1 << 4)) { /* in4 */ if (data->has_in & (1 << 4)) { /* in4 */
if ((err = sysfs_create_group(&pdev->dev.kobj, if ((err = sysfs_create_group(&pdev->dev.kobj,
&f71805f_group_optin[0]))) &f71805f_group_optin[0])))
...@@ -1219,6 +1227,8 @@ static int __devinit f71805f_probe(struct platform_device *pdev) ...@@ -1219,6 +1227,8 @@ static int __devinit f71805f_probe(struct platform_device *pdev)
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_optin[i]); sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_optin[i]);
sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq); sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq);
exit_release_region:
release_region(res->start + ADDR_REG_OFFSET, 2);
exit_free: exit_free:
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
kfree(data); kfree(data);
...@@ -1229,6 +1239,7 @@ static int __devinit f71805f_probe(struct platform_device *pdev) ...@@ -1229,6 +1239,7 @@ static int __devinit f71805f_probe(struct platform_device *pdev)
static int __devexit f71805f_remove(struct platform_device *pdev) static int __devexit f71805f_remove(struct platform_device *pdev)
{ {
struct f71805f_data *data = platform_get_drvdata(pdev); struct f71805f_data *data = platform_get_drvdata(pdev);
struct resource *res;
int i; int i;
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
...@@ -1239,6 +1250,9 @@ static int __devexit f71805f_remove(struct platform_device *pdev) ...@@ -1239,6 +1250,9 @@ static int __devexit f71805f_remove(struct platform_device *pdev)
sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq); sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq);
kfree(data); kfree(data);
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
release_region(res->start + ADDR_REG_OFFSET, 2);
return 0; return 0;
} }
......
...@@ -166,16 +166,16 @@ static struct vrm_model vrm_models[] = { ...@@ -166,16 +166,16 @@ static struct vrm_model vrm_models[] = {
{X86_VENDOR_INTEL, 0x6, 0xE, ANY, 14}, /* Intel Core (65 nm) */ {X86_VENDOR_INTEL, 0x6, 0xE, ANY, 14}, /* Intel Core (65 nm) */
{X86_VENDOR_INTEL, 0x6, 0xF, ANY, 110}, /* Intel Conroe */ {X86_VENDOR_INTEL, 0x6, 0xF, ANY, 110}, /* Intel Conroe */
{X86_VENDOR_INTEL, 0x6, ANY, ANY, 82}, /* any P6 */ {X86_VENDOR_INTEL, 0x6, ANY, ANY, 82}, /* any P6 */
{X86_VENDOR_INTEL, 0x7, ANY, ANY, 0}, /* Itanium */
{X86_VENDOR_INTEL, 0xF, 0x0, ANY, 90}, /* P4 */ {X86_VENDOR_INTEL, 0xF, 0x0, ANY, 90}, /* P4 */
{X86_VENDOR_INTEL, 0xF, 0x1, ANY, 90}, /* P4 Willamette */ {X86_VENDOR_INTEL, 0xF, 0x1, ANY, 90}, /* P4 Willamette */
{X86_VENDOR_INTEL, 0xF, 0x2, ANY, 90}, /* P4 Northwood */ {X86_VENDOR_INTEL, 0xF, 0x2, ANY, 90}, /* P4 Northwood */
{X86_VENDOR_INTEL, 0xF, ANY, ANY, 100}, /* Prescott and above assume VRD 10 */ {X86_VENDOR_INTEL, 0xF, ANY, ANY, 100}, /* Prescott and above assume VRD 10 */
{X86_VENDOR_INTEL, 0x10, ANY, ANY, 0}, /* Itanium 2 */
{X86_VENDOR_CENTAUR, 0x6, 0x7, ANY, 85}, /* Eden ESP/Ezra */ {X86_VENDOR_CENTAUR, 0x6, 0x7, ANY, 85}, /* Eden ESP/Ezra */
{X86_VENDOR_CENTAUR, 0x6, 0x8, 0x7, 85}, /* Ezra T */ {X86_VENDOR_CENTAUR, 0x6, 0x8, 0x7, 85}, /* Ezra T */
{X86_VENDOR_CENTAUR, 0x6, 0x9, 0x7, 85}, /* Nemiah */ {X86_VENDOR_CENTAUR, 0x6, 0x9, 0x7, 85}, /* Nemiah */
{X86_VENDOR_CENTAUR, 0x6, 0x9, ANY, 17}, /* C3-M */ {X86_VENDOR_CENTAUR, 0x6, 0x9, ANY, 17}, /* C3-M, Eden-N */
{X86_VENDOR_CENTAUR, 0x6, 0xA, 0x7, 0}, /* No information */
{X86_VENDOR_CENTAUR, 0x6, 0xA, ANY, 13}, /* C7, Esther */
{X86_VENDOR_UNKNOWN, ANY, ANY, ANY, 0} /* stop here */ {X86_VENDOR_UNKNOWN, ANY, ANY, ANY, 0} /* stop here */
}; };
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include "lm75.h" #include "lm75.h"
...@@ -39,10 +40,12 @@ I2C_CLIENT_INSMOD_1(lm75); ...@@ -39,10 +40,12 @@ I2C_CLIENT_INSMOD_1(lm75);
/* Many LM75 constants specified below */ /* Many LM75 constants specified below */
/* The LM75 registers */ /* The LM75 registers */
#define LM75_REG_TEMP 0x00
#define LM75_REG_CONF 0x01 #define LM75_REG_CONF 0x01
#define LM75_REG_TEMP_HYST 0x02 static const u8 LM75_REG_TEMP[3] = {
#define LM75_REG_TEMP_OS 0x03 0x00, /* input */
0x03, /* max */
0x02, /* hyst */
};
/* Each client has this additional data */ /* Each client has this additional data */
struct lm75_data { struct lm75_data {
...@@ -51,9 +54,10 @@ struct lm75_data { ...@@ -51,9 +54,10 @@ struct lm75_data {
struct mutex update_lock; struct mutex update_lock;
char valid; /* !=0 if following fields are valid */ char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */ unsigned long last_updated; /* In jiffies */
u16 temp_input; /* Register values */ u16 temp[3]; /* Register values,
u16 temp_max; 0 = input
u16 temp_hyst; 1 = max
2 = hyst */
}; };
static int lm75_attach_adapter(struct i2c_adapter *adapter); static int lm75_attach_adapter(struct i2c_adapter *adapter);
...@@ -75,35 +79,36 @@ static struct i2c_driver lm75_driver = { ...@@ -75,35 +79,36 @@ static struct i2c_driver lm75_driver = {
.detach_client = lm75_detach_client, .detach_client = lm75_detach_client,
}; };
#define show(value) \ static ssize_t show_temp(struct device *dev, struct device_attribute *da,
static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ char *buf)
{ \ {
struct lm75_data *data = lm75_update_device(dev); \ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->value)); \ struct lm75_data *data = lm75_update_device(dev);
return sprintf(buf, "%d\n",
LM75_TEMP_FROM_REG(data->temp[attr->index]));
} }
show(temp_max);
show(temp_hyst); static ssize_t set_temp(struct device *dev, struct device_attribute *da,
show(temp_input); const char *buf, size_t count)
{
#define set(value, reg) \ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ struct i2c_client *client = to_i2c_client(dev);
{ \ struct lm75_data *data = i2c_get_clientdata(client);
struct i2c_client *client = to_i2c_client(dev); \ int nr = attr->index;
struct lm75_data *data = i2c_get_clientdata(client); \ unsigned long temp = simple_strtoul(buf, NULL, 10);
int temp = simple_strtoul(buf, NULL, 10); \
\ mutex_lock(&data->update_lock);
mutex_lock(&data->update_lock); \ data->temp[nr] = LM75_TEMP_TO_REG(temp);
data->value = LM75_TEMP_TO_REG(temp); \ lm75_write_value(client, LM75_REG_TEMP[nr], data->temp[nr]);
lm75_write_value(client, reg, data->value); \ mutex_unlock(&data->update_lock);
mutex_unlock(&data->update_lock); \ return count;
return count; \
} }
set(temp_max, LM75_REG_TEMP_OS);
set(temp_hyst, LM75_REG_TEMP_HYST);
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max); static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
static DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst); show_temp, set_temp, 1);
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL); static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
show_temp, set_temp, 2);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
static int lm75_attach_adapter(struct i2c_adapter *adapter) static int lm75_attach_adapter(struct i2c_adapter *adapter)
{ {
...@@ -113,9 +118,9 @@ static int lm75_attach_adapter(struct i2c_adapter *adapter) ...@@ -113,9 +118,9 @@ static int lm75_attach_adapter(struct i2c_adapter *adapter)
} }
static struct attribute *lm75_attributes[] = { static struct attribute *lm75_attributes[] = {
&dev_attr_temp1_input.attr, &sensor_dev_attr_temp1_input.dev_attr.attr,
&dev_attr_temp1_max.attr, &sensor_dev_attr_temp1_max.dev_attr.attr,
&dev_attr_temp1_max_hyst.attr, &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
NULL NULL
}; };
...@@ -283,11 +288,12 @@ static struct lm75_data *lm75_update_device(struct device *dev) ...@@ -283,11 +288,12 @@ static struct lm75_data *lm75_update_device(struct device *dev)
if (time_after(jiffies, data->last_updated + HZ + HZ / 2) if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) { || !data->valid) {
int i;
dev_dbg(&client->dev, "Starting lm75 update\n"); dev_dbg(&client->dev, "Starting lm75 update\n");
data->temp_input = lm75_read_value(client, LM75_REG_TEMP); for (i = 0; i < ARRAY_SIZE(data->temp); i++)
data->temp_max = lm75_read_value(client, LM75_REG_TEMP_OS); data->temp[i] = lm75_read_value(client,
data->temp_hyst = lm75_read_value(client, LM75_REG_TEMP_HYST); LM75_REG_TEMP[i]);
data->last_updated = jiffies; data->last_updated = jiffies;
data->valid = 1; data->valid = 1;
} }
......
此差异已折叠。
...@@ -747,6 +747,7 @@ static int lm87_detect(struct i2c_adapter *adapter, int address, int kind) ...@@ -747,6 +747,7 @@ static int lm87_detect(struct i2c_adapter *adapter, int address, int kind)
} }
if (!(data->channel & CHAN_NO_VID)) { if (!(data->channel & CHAN_NO_VID)) {
data->vrm = vid_which_vrm();
if ((err = device_create_file(&new_client->dev, if ((err = device_create_file(&new_client->dev,
&dev_attr_cpu0_vid)) &dev_attr_cpu0_vid))
|| (err = device_create_file(&new_client->dev, || (err = device_create_file(&new_client->dev,
...@@ -779,7 +780,6 @@ static void lm87_init_client(struct i2c_client *client) ...@@ -779,7 +780,6 @@ static void lm87_init_client(struct i2c_client *client)
u8 config; u8 config;
data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE); data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE);
data->vrm = vid_which_vrm();
config = lm87_read_value(client, LM87_REG_CONFIG); config = lm87_read_value(client, LM87_REG_CONFIG);
if (!(config & 0x01)) { if (!(config & 0x01)) {
......
/*
* max6650.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring.
*
* (C) 2007 by Hans J. Koch <hjk@linutronix.de>
*
* based on code written by John Morris <john.morris@spirentcom.com>
* Copyright (c) 2003 Spirent Communications
* and Claus Gindhart <claus.gindhart@kontron.com>
*
* This module has only been tested with the MAX6650 chip. It should
* also work with the MAX6651. It does not distinguish max6650 and max6651
* chips.
*
* Tha datasheet was last seen at:
*
* http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf
*
* 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/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>
/*
* Addresses to scan. There are four disjoint possibilities, by pin config.
*/
static unsigned short normal_i2c[] = {0x1b, 0x1f, 0x48, 0x4b, I2C_CLIENT_END};
/*
* Insmod parameters
*/
/* fan_voltage: 5=5V fan, 12=12V fan, 0=don't change */
static int fan_voltage;
/* prescaler: Possible values are 1, 2, 4, 8, 16 or 0 for don't change */
static int prescaler;
/* clock: The clock frequency of the chip the driver should assume */
static int clock = 254000;
module_param(fan_voltage, int, S_IRUGO);
module_param(prescaler, int, S_IRUGO);
module_param(clock, int, S_IRUGO);
I2C_CLIENT_INSMOD_1(max6650);
/*
* MAX 6650/6651 registers
*/
#define MAX6650_REG_SPEED 0x00
#define MAX6650_REG_CONFIG 0x02
#define MAX6650_REG_GPIO_DEF 0x04
#define MAX6650_REG_DAC 0x06
#define MAX6650_REG_ALARM_EN 0x08
#define MAX6650_REG_ALARM 0x0A
#define MAX6650_REG_TACH0 0x0C
#define MAX6650_REG_TACH1 0x0E
#define MAX6650_REG_TACH2 0x10
#define MAX6650_REG_TACH3 0x12
#define MAX6650_REG_GPIO_STAT 0x14
#define MAX6650_REG_COUNT 0x16
/*
* Config register bits
*/
#define MAX6650_CFG_V12 0x08
#define MAX6650_CFG_PRESCALER_MASK 0x07
#define MAX6650_CFG_PRESCALER_2 0x01
#define MAX6650_CFG_PRESCALER_4 0x02
#define MAX6650_CFG_PRESCALER_8 0x03
#define MAX6650_CFG_PRESCALER_16 0x04
#define MAX6650_CFG_MODE_MASK 0x30
#define MAX6650_CFG_MODE_ON 0x00
#define MAX6650_CFG_MODE_OFF 0x10
#define MAX6650_CFG_MODE_CLOSED_LOOP 0x20
#define MAX6650_CFG_MODE_OPEN_LOOP 0x30
#define MAX6650_COUNT_MASK 0x03
/* Minimum and maximum values of the FAN-RPM */
#define FAN_RPM_MIN 240
#define FAN_RPM_MAX 30000
#define DIV_FROM_REG(reg) (1 << (reg & 7))
static int max6650_attach_adapter(struct i2c_adapter *adapter);
static int max6650_detect(struct i2c_adapter *adapter, int address, int kind);
static int max6650_init_client(struct i2c_client *client);
static int max6650_detach_client(struct i2c_client *client);
static struct max6650_data *max6650_update_device(struct device *dev);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver max6650_driver = {
.driver = {
.name = "max6650",
},
.attach_adapter = max6650_attach_adapter,
.detach_client = max6650_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct max6650_data
{
struct i2c_client client;
struct class_device *class_dev;
struct mutex update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
/* register values */
u8 speed;
u8 config;
u8 tach[4];
u8 count;
u8 dac;
};
static ssize_t get_fan(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max6650_data *data = max6650_update_device(dev);
int rpm;
/*
* Calculation details:
*
* Each tachometer counts over an interval given by the "count"
* register (0.25, 0.5, 1 or 2 seconds). This module assumes
* that the fans produce two pulses per revolution (this seems
* to be the most common).
*/
rpm = ((data->tach[attr->index] * 120) / DIV_FROM_REG(data->count));
return sprintf(buf, "%d\n", rpm);
}
/*
* Set the fan speed to the specified RPM (or read back the RPM setting).
* This works in closed loop mode only. Use pwm1 for open loop speed setting.
*
* The MAX6650/1 will automatically control fan speed when in closed loop
* mode.
*
* Assumptions:
*
* 1) The MAX6650/1 internal 254kHz clock frequency is set correctly. Use
* the clock module parameter if you need to fine tune this.
*
* 2) The prescaler (low three bits of the config register) has already
* been set to an appropriate value. Use the prescaler module parameter
* if your BIOS doesn't initialize the chip properly.
*
* The relevant equations are given on pages 21 and 22 of the datasheet.
*
* From the datasheet, the relevant equation when in regulation is:
*
* [fCLK / (128 x (KTACH + 1))] = 2 x FanSpeed / KSCALE
*
* where:
*
* fCLK is the oscillator frequency (either the 254kHz internal
* oscillator or the externally applied clock)
*
* KTACH is the value in the speed register
*
* FanSpeed is the speed of the fan in rps
*
* KSCALE is the prescaler value (1, 2, 4, 8, or 16)
*
* When reading, we need to solve for FanSpeed. When writing, we need to
* solve for KTACH.
*
* Note: this tachometer is completely separate from the tachometers
* used to measure the fan speeds. Only one fan's speed (fan1) is
* controlled.
*/
static ssize_t get_target(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct max6650_data *data = max6650_update_device(dev);
int kscale, ktach, rpm;
/*
* Use the datasheet equation:
*
* FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)]
*
* then multiply by 60 to give rpm.
*/
kscale = DIV_FROM_REG(data->config);
ktach = data->speed;
rpm = 60 * kscale * clock / (256 * (ktach + 1));
return sprintf(buf, "%d\n", rpm);
}
static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct max6650_data *data = i2c_get_clientdata(client);
int rpm = simple_strtoul(buf, NULL, 10);
int kscale, ktach;
rpm = SENSORS_LIMIT(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
/*
* Divide the required speed by 60 to get from rpm to rps, then
* use the datasheet equation:
*
* KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1
*/
mutex_lock(&data->update_lock);
kscale = DIV_FROM_REG(data->config);
ktach = ((clock * kscale) / (256 * rpm / 60)) - 1;
if (ktach < 0)
ktach = 0;
if (ktach > 255)
ktach = 255;
data->speed = ktach;
i2c_smbus_write_byte_data(client, MAX6650_REG_SPEED, data->speed);
mutex_unlock(&data->update_lock);
return count;
}
/*
* Get/set the fan speed in open loop mode using pwm1 sysfs file.
* Speed is given as a relative value from 0 to 255, where 255 is maximum
* speed. Note that this is done by writing directly to the chip's DAC,
* it won't change the closed loop speed set by fan1_target.
* Also note that due to rounding errors it is possible that you don't read
* back exactly the value you have set.
*/
static ssize_t get_pwm(struct device *dev, struct device_attribute *devattr,
char *buf)
{
int pwm;
struct max6650_data *data = max6650_update_device(dev);
/* Useful range for dac is 0-180 for 12V fans and 0-76 for 5V fans.
Lower DAC values mean higher speeds. */
if (data->config & MAX6650_CFG_V12)
pwm = 255 - (255 * (int)data->dac)/180;
else
pwm = 255 - (255 * (int)data->dac)/76;
if (pwm < 0)
pwm = 0;
return sprintf(buf, "%d\n", pwm);
}
static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct max6650_data *data = i2c_get_clientdata(client);
int pwm = simple_strtoul(buf, NULL, 10);
pwm = SENSORS_LIMIT(pwm, 0, 255);
mutex_lock(&data->update_lock);
if (data->config & MAX6650_CFG_V12)
data->dac = 180 - (180 * pwm)/255;
else
data->dac = 76 - (76 * pwm)/255;
i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac);
mutex_unlock(&data->update_lock);
return count;
}
/*
* Get/Set controller mode:
* Possible values:
* 0 = Fan always on
* 1 = Open loop, Voltage is set according to speed, not regulated.
* 2 = Closed loop, RPM for all fans regulated by fan1 tachometer
*/
static ssize_t get_enable(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct max6650_data *data = max6650_update_device(dev);
int mode = (data->config & MAX6650_CFG_MODE_MASK) >> 4;
int sysfs_modes[4] = {0, 1, 2, 1};
return sprintf(buf, "%d\n", sysfs_modes[mode]);
}
static ssize_t set_enable(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct max6650_data *data = i2c_get_clientdata(client);
int mode = simple_strtoul(buf, NULL, 10);
int max6650_modes[3] = {0, 3, 2};
if ((mode < 0)||(mode > 2)) {
dev_err(&client->dev,
"illegal value for pwm1_enable (%d)\n", mode);
return -EINVAL;
}
mutex_lock(&data->update_lock);
data->config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG);
data->config = (data->config & ~MAX6650_CFG_MODE_MASK)
| (max6650_modes[mode] << 4);
i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, data->config);
mutex_unlock(&data->update_lock);
return count;
}
/*
* Read/write functions for fan1_div sysfs file. The MAX6650 has no such
* divider. We handle this by converting between divider and counttime:
*
* (counttime == k) <==> (divider == 2^k), k = 0, 1, 2, or 3
*
* Lower values of k allow to connect a faster fan without the risk of
* counter overflow. The price is lower resolution. You can also set counttime
* using the module parameter. Note that the module parameter "prescaler" also
* influences the behaviour. Unfortunately, there's no sysfs attribute
* defined for that. See the data sheet for details.
*/
static ssize_t get_div(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct max6650_data *data = max6650_update_device(dev);
return sprintf(buf, "%d\n", DIV_FROM_REG(data->count));
}
static ssize_t set_div(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct max6650_data *data = i2c_get_clientdata(client);
int div = simple_strtoul(buf, NULL, 10);
mutex_lock(&data->update_lock);
switch (div) {
case 1:
data->count = 0;
break;
case 2:
data->count = 1;
break;
case 4:
data->count = 2;
break;
case 8:
data->count = 3;
break;
default:
dev_err(&client->dev,
"illegal value for fan divider (%d)\n", div);
return -EINVAL;
}
i2c_smbus_write_byte_data(client, MAX6650_REG_COUNT, data->count);
mutex_unlock(&data->update_lock);
return count;
}
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0);
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1);
static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2);
static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, get_fan, NULL, 3);
static DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, get_target, set_target);
static DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, get_div, set_div);
static DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, get_enable, set_enable);
static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm);
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,
&dev_attr_pwm1.attr,
NULL
};
static struct attribute_group max6650_attr_grp = {
.attrs = max6650_attrs,
};
/*
* Real code
*/
static int max6650_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON)) {
dev_dbg(&adapter->dev,
"FATAL: max6650_attach_adapter class HWMON not set\n");
return 0;
}
return i2c_probe(adapter, &addr_data, max6650_detect);
}
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
*/
static int max6650_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *client;
struct max6650_data *data;
int err = -ENODEV;
dev_dbg(&adapter->dev, "max6650_detect called, kind = %d\n", kind);
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 0;
}
if (!(data = kzalloc(sizeof(struct max6650_data), GFP_KERNEL))) {
dev_err(&adapter->dev, "max6650: out of memory.\n");
return -ENOMEM;
}
client = &data->client;
i2c_set_clientdata(client, data);
client->addr = address;
client->adapter = adapter;
client->driver = &max6650_driver;
/*
* Now we do the remaining detection. A negative kind means that
* the driver was loaded with no force parameter (default), so we
* must both detect and identify the chip (actually there is only
* one possible kind of chip for now, max6650). A zero kind means that
* the driver was loaded with the force parameter, the detection
* step shall be skipped. A positive kind means that the driver
* was loaded with the force parameter and a given kind of chip is
* requested, so both the detection and the identification steps
* are skipped.
*
* Currently I can find no way to distinguish between a MAX6650 and
* a MAX6651. This driver has only been tried on the former.
*/
if ((kind < 0) &&
( (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);
goto err_free;
}
dev_info(&adapter->dev, "max6650: chip found at 0x%02x.\n", address);
strlcpy(client->name, "max6650", I2C_NAME_SIZE);
mutex_init(&data->update_lock);
if ((err = i2c_attach_client(client))) {
dev_err(&adapter->dev, "max6650: failed to attach client.\n");
goto err_free;
}
/*
* Initialize the max6650 chip
*/
if (max6650_init_client(client))
goto err_detach;
err = sysfs_create_group(&client->dev.kobj, &max6650_attr_grp);
if (err)
goto err_detach;
data->class_dev = hwmon_device_register(&client->dev);
if (!IS_ERR(data->class_dev))
return 0;
err = PTR_ERR(data->class_dev);
dev_err(&client->dev, "error registering hwmon device.\n");
sysfs_remove_group(&client->dev.kobj, &max6650_attr_grp);
err_detach:
i2c_detach_client(client);
err_free:
kfree(data);
return err;
}
static int max6650_detach_client(struct i2c_client *client)
{
struct max6650_data *data = i2c_get_clientdata(client);
int err;
sysfs_remove_group(&client->dev.kobj, &max6650_attr_grp);
hwmon_device_unregister(data->class_dev);
err = i2c_detach_client(client);
if (!err)
kfree(data);
return err;
}
static int max6650_init_client(struct i2c_client *client)
{
struct max6650_data *data = i2c_get_clientdata(client);
int config;
int err = -EIO;
config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG);
if (config < 0) {
dev_err(&client->dev, "Error reading config, aborting.\n");
return err;
}
switch (fan_voltage) {
case 0:
break;
case 5:
config &= ~MAX6650_CFG_V12;
break;
case 12:
config |= MAX6650_CFG_V12;
break;
default:
dev_err(&client->dev,
"illegal value for fan_voltage (%d)\n",
fan_voltage);
}
dev_info(&client->dev, "Fan voltage is set to %dV.\n",
(config & MAX6650_CFG_V12) ? 12 : 5);
switch (prescaler) {
case 0:
break;
case 1:
config &= ~MAX6650_CFG_PRESCALER_MASK;
break;
case 2:
config = (config & ~MAX6650_CFG_PRESCALER_MASK)
| MAX6650_CFG_PRESCALER_2;
break;
case 4:
config = (config & ~MAX6650_CFG_PRESCALER_MASK)
| MAX6650_CFG_PRESCALER_4;
break;
case 8:
config = (config & ~MAX6650_CFG_PRESCALER_MASK)
| MAX6650_CFG_PRESCALER_8;
break;
case 16:
config = (config & ~MAX6650_CFG_PRESCALER_MASK)
| MAX6650_CFG_PRESCALER_16;
break;
default:
dev_err(&client->dev,
"illegal value for prescaler (%d)\n",
prescaler);
}
dev_info(&client->dev, "Prescaler is set to %d.\n",
1 << (config & MAX6650_CFG_PRESCALER_MASK));
/* If mode is set to "full off", we change it to "open loop" and
* set DAC to 255, which has the same effect. We do this because
* there's no "full off" mode defined in hwmon specifcations.
*/
if ((config & MAX6650_CFG_MODE_MASK) == MAX6650_CFG_MODE_OFF) {
dev_dbg(&client->dev, "Change mode to open loop, full off.\n");
config = (config & ~MAX6650_CFG_MODE_MASK)
| MAX6650_CFG_MODE_OPEN_LOOP;
if (i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, 255)) {
dev_err(&client->dev, "DAC write error, aborting.\n");
return err;
}
}
if (i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, config)) {
dev_err(&client->dev, "Config write error, aborting.\n");
return err;
}
data->config = config;
data->count = i2c_smbus_read_byte_data(client, MAX6650_REG_COUNT);
return 0;
}
static const u8 tach_reg[] = {
MAX6650_REG_TACH0,
MAX6650_REG_TACH1,
MAX6650_REG_TACH2,
MAX6650_REG_TACH3,
};
static struct max6650_data *max6650_update_device(struct device *dev)
{
int i;
struct i2c_client *client = to_i2c_client(dev);
struct max6650_data *data = i2c_get_clientdata(client);
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
data->speed = i2c_smbus_read_byte_data(client,
MAX6650_REG_SPEED);
data->config = i2c_smbus_read_byte_data(client,
MAX6650_REG_CONFIG);
for (i = 0; i < 4; i++) {
data->tach[i] = i2c_smbus_read_byte_data(client,
tach_reg[i]);
}
data->count = i2c_smbus_read_byte_data(client,
MAX6650_REG_COUNT);
data->dac = i2c_smbus_read_byte_data(client, MAX6650_REG_DAC);
data->last_updated = jiffies;
data->valid = 1;
}
mutex_unlock(&data->update_lock);
return data;
}
static int __init sensors_max6650_init(void)
{
return i2c_add_driver(&max6650_driver);
}
static void __exit sensors_max6650_exit(void)
{
i2c_del_driver(&max6650_driver);
}
MODULE_AUTHOR("Hans J. Koch");
MODULE_DESCRIPTION("MAX6650 sensor driver");
MODULE_LICENSE("GPL");
module_init(sensors_max6650_init);
module_exit(sensors_max6650_exit);
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/ioport.h>
#include <asm/io.h> #include <asm/io.h>
static struct platform_device *pdev; static struct platform_device *pdev;
...@@ -429,6 +430,12 @@ static int __devinit pc87427_probe(struct platform_device *pdev) ...@@ -429,6 +430,12 @@ static int __devinit pc87427_probe(struct platform_device *pdev)
/* This will need to be revisited when we add support for /* This will need to be revisited when we add support for
temperature and voltage monitoring. */ temperature and voltage monitoring. */
res = platform_get_resource(pdev, IORESOURCE_IO, 0); res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!request_region(res->start, res->end - res->start + 1, DRVNAME)) {
err = -EBUSY;
dev_err(&pdev->dev, "Failed to request region 0x%lx-0x%lx\n",
(unsigned long)res->start, (unsigned long)res->end);
goto exit_kfree;
}
data->address[0] = res->start; data->address[0] = res->start;
mutex_init(&data->lock); mutex_init(&data->lock);
...@@ -438,7 +445,7 @@ static int __devinit pc87427_probe(struct platform_device *pdev) ...@@ -438,7 +445,7 @@ static int __devinit pc87427_probe(struct platform_device *pdev)
/* Register sysfs hooks */ /* Register sysfs hooks */
if ((err = device_create_file(&pdev->dev, &dev_attr_name))) if ((err = device_create_file(&pdev->dev, &dev_attr_name)))
goto exit_kfree; goto exit_release_region;
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
if (!(data->fan_enabled & (1 << i))) if (!(data->fan_enabled & (1 << i)))
continue; continue;
...@@ -462,6 +469,8 @@ static int __devinit pc87427_probe(struct platform_device *pdev) ...@@ -462,6 +469,8 @@ static int __devinit pc87427_probe(struct platform_device *pdev)
continue; continue;
sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]); sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]);
} }
exit_release_region:
release_region(res->start, res->end - res->start + 1);
exit_kfree: exit_kfree:
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
kfree(data); kfree(data);
...@@ -472,6 +481,7 @@ static int __devinit pc87427_probe(struct platform_device *pdev) ...@@ -472,6 +481,7 @@ static int __devinit pc87427_probe(struct platform_device *pdev)
static int __devexit pc87427_remove(struct platform_device *pdev) static int __devexit pc87427_remove(struct platform_device *pdev)
{ {
struct pc87427_data *data = platform_get_drvdata(pdev); struct pc87427_data *data = platform_get_drvdata(pdev);
struct resource *res;
int i; int i;
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
...@@ -484,6 +494,9 @@ static int __devexit pc87427_remove(struct platform_device *pdev) ...@@ -484,6 +494,9 @@ static int __devexit pc87427_remove(struct platform_device *pdev)
} }
kfree(data); kfree(data);
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
release_region(res->start, res->end - res->start + 1);
return 0; return 0;
} }
......
...@@ -30,16 +30,17 @@ ...@@ -30,16 +30,17 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/i2c.h> #include <linux/platform_device.h>
#include <linux/i2c-isa.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <asm/io.h> #include <asm/io.h>
/* Address is autodetected, there is no default value */ static struct platform_device *pdev;
static unsigned short address;
#define DRVNAME "smsc47b397"
/* Super-I/0 registers and commands */ /* Super-I/0 registers and commands */
...@@ -91,7 +92,8 @@ static u8 smsc47b397_reg_temp[] = {0x25, 0x26, 0x27, 0x80}; ...@@ -91,7 +92,8 @@ static u8 smsc47b397_reg_temp[] = {0x25, 0x26, 0x27, 0x80};
#define SMSC47B397_REG_FAN_MSB(nr) (0x29 + 2 * (nr)) #define SMSC47B397_REG_FAN_MSB(nr) (0x29 + 2 * (nr))
struct smsc47b397_data { struct smsc47b397_data {
struct i2c_client client; unsigned short addr;
const char *name;
struct class_device *class_dev; struct class_device *class_dev;
struct mutex lock; struct mutex lock;
...@@ -104,45 +106,43 @@ struct smsc47b397_data { ...@@ -104,45 +106,43 @@ struct smsc47b397_data {
u8 temp[4]; u8 temp[4];
}; };
static int smsc47b397_read_value(struct i2c_client *client, u8 reg) static int smsc47b397_read_value(struct smsc47b397_data* data, u8 reg)
{ {
struct smsc47b397_data *data = i2c_get_clientdata(client);
int res; int res;
mutex_lock(&data->lock); mutex_lock(&data->lock);
outb(reg, client->addr); outb(reg, data->addr);
res = inb_p(client->addr + 1); res = inb_p(data->addr + 1);
mutex_unlock(&data->lock); mutex_unlock(&data->lock);
return res; return res;
} }
static struct smsc47b397_data *smsc47b397_update_device(struct device *dev) static struct smsc47b397_data *smsc47b397_update_device(struct device *dev)
{ {
struct i2c_client *client = to_i2c_client(dev); struct smsc47b397_data *data = dev_get_drvdata(dev);
struct smsc47b397_data *data = i2c_get_clientdata(client);
int i; int i;
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
dev_dbg(&client->dev, "starting device update...\n"); dev_dbg(dev, "starting device update...\n");
/* 4 temperature inputs, 4 fan inputs */ /* 4 temperature inputs, 4 fan inputs */
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
data->temp[i] = smsc47b397_read_value(client, data->temp[i] = smsc47b397_read_value(data,
SMSC47B397_REG_TEMP(i)); SMSC47B397_REG_TEMP(i));
/* must read LSB first */ /* must read LSB first */
data->fan[i] = smsc47b397_read_value(client, data->fan[i] = smsc47b397_read_value(data,
SMSC47B397_REG_FAN_LSB(i)); SMSC47B397_REG_FAN_LSB(i));
data->fan[i] |= smsc47b397_read_value(client, data->fan[i] |= smsc47b397_read_value(data,
SMSC47B397_REG_FAN_MSB(i)) << 8; SMSC47B397_REG_FAN_MSB(i)) << 8;
} }
data->last_updated = jiffies; data->last_updated = jiffies;
data->valid = 1; data->valid = 1;
dev_dbg(&client->dev, "... device update complete\n"); dev_dbg(dev, "... device update complete\n");
} }
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
...@@ -157,24 +157,18 @@ static int temp_from_reg(u8 reg) ...@@ -157,24 +157,18 @@ static int temp_from_reg(u8 reg)
return (s8)reg * 1000; return (s8)reg * 1000;
} }
/* 0 <= nr <= 3 */ static ssize_t show_temp(struct device *dev, struct device_attribute
static ssize_t show_temp(struct device *dev, char *buf, int nr) *devattr, char *buf)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct smsc47b397_data *data = smsc47b397_update_device(dev); struct smsc47b397_data *data = smsc47b397_update_device(dev);
return sprintf(buf, "%d\n", temp_from_reg(data->temp[nr])); return sprintf(buf, "%d\n", temp_from_reg(data->temp[attr->index]));
} }
#define sysfs_temp(num) \ static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
static ssize_t show_temp##num(struct device *dev, struct device_attribute *attr, char *buf) \ static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
{ \ static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
return show_temp(dev, buf, num-1); \ static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
} \
static DEVICE_ATTR(temp##num##_input, S_IRUGO, show_temp##num, NULL)
sysfs_temp(1);
sysfs_temp(2);
sysfs_temp(3);
sysfs_temp(4);
/* FAN: 1 RPM/bit /* FAN: 1 RPM/bit
REG: count of 90kHz pulses / revolution */ REG: count of 90kHz pulses / revolution */
...@@ -183,35 +177,37 @@ static int fan_from_reg(u16 reg) ...@@ -183,35 +177,37 @@ static int fan_from_reg(u16 reg)
return 90000 * 60 / reg; return 90000 * 60 / reg;
} }
/* 0 <= nr <= 3 */ static ssize_t show_fan(struct device *dev, struct device_attribute
static ssize_t show_fan(struct device *dev, char *buf, int nr) *devattr, char *buf)
{ {
struct smsc47b397_data *data = smsc47b397_update_device(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
return sprintf(buf, "%d\n", fan_from_reg(data->fan[nr])); struct smsc47b397_data *data = smsc47b397_update_device(dev);
return sprintf(buf, "%d\n", fan_from_reg(data->fan[attr->index]));
} }
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2);
static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3);
#define sysfs_fan(num) \ static ssize_t show_name(struct device *dev, struct device_attribute
static ssize_t show_fan##num(struct device *dev, struct device_attribute *attr, char *buf) \ *devattr, char *buf)
{ \ {
return show_fan(dev, buf, num-1); \ struct smsc47b397_data *data = dev_get_drvdata(dev);
} \ return sprintf(buf, "%s\n", data->name);
static DEVICE_ATTR(fan##num##_input, S_IRUGO, show_fan##num, NULL) }
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
sysfs_fan(1);
sysfs_fan(2);
sysfs_fan(3);
sysfs_fan(4);
static struct attribute *smsc47b397_attributes[] = { static struct attribute *smsc47b397_attributes[] = {
&dev_attr_temp1_input.attr, &sensor_dev_attr_temp1_input.dev_attr.attr,
&dev_attr_temp2_input.attr, &sensor_dev_attr_temp2_input.dev_attr.attr,
&dev_attr_temp3_input.attr, &sensor_dev_attr_temp3_input.dev_attr.attr,
&dev_attr_temp4_input.attr, &sensor_dev_attr_temp4_input.dev_attr.attr,
&dev_attr_fan1_input.attr, &sensor_dev_attr_fan1_input.dev_attr.attr,
&dev_attr_fan2_input.attr, &sensor_dev_attr_fan2_input.dev_attr.attr,
&dev_attr_fan3_input.attr, &sensor_dev_attr_fan3_input.dev_attr.attr,
&dev_attr_fan4_input.attr, &sensor_dev_attr_fan4_input.dev_attr.attr,
&dev_attr_name.attr,
NULL NULL
}; };
...@@ -219,44 +215,44 @@ static const struct attribute_group smsc47b397_group = { ...@@ -219,44 +215,44 @@ static const struct attribute_group smsc47b397_group = {
.attrs = smsc47b397_attributes, .attrs = smsc47b397_attributes,
}; };
static int smsc47b397_detach_client(struct i2c_client *client) static int __devexit smsc47b397_remove(struct platform_device *pdev)
{ {
struct smsc47b397_data *data = i2c_get_clientdata(client); struct smsc47b397_data *data = platform_get_drvdata(pdev);
int err; struct resource *res;
hwmon_device_unregister(data->class_dev); hwmon_device_unregister(data->class_dev);
sysfs_remove_group(&client->dev.kobj, &smsc47b397_group); sysfs_remove_group(&pdev->dev.kobj, &smsc47b397_group);
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if ((err = i2c_detach_client(client))) release_region(res->start, SMSC_EXTENT);
return err;
release_region(client->addr, SMSC_EXTENT);
kfree(data); kfree(data);
return 0; return 0;
} }
static int smsc47b397_detect(struct i2c_adapter *adapter); static int smsc47b397_probe(struct platform_device *pdev);
static struct i2c_driver smsc47b397_driver = { static struct platform_driver smsc47b397_driver = {
.driver = { .driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "smsc47b397", .name = DRVNAME,
}, },
.attach_adapter = smsc47b397_detect, .probe = smsc47b397_probe,
.detach_client = smsc47b397_detach_client, .remove = __devexit_p(smsc47b397_remove),
}; };
static int smsc47b397_detect(struct i2c_adapter *adapter) static int __devinit smsc47b397_probe(struct platform_device *pdev)
{ {
struct i2c_client *new_client; struct device *dev = &pdev->dev;
struct smsc47b397_data *data; struct smsc47b397_data *data;
struct resource *res;
int err = 0; int err = 0;
if (!request_region(address, SMSC_EXTENT, res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!request_region(res->start, SMSC_EXTENT,
smsc47b397_driver.driver.name)) { smsc47b397_driver.driver.name)) {
dev_err(&adapter->dev, "Region 0x%x already in use!\n", dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
address); (unsigned long)res->start,
(unsigned long)res->start + SMSC_EXTENT - 1);
return -EBUSY; return -EBUSY;
} }
...@@ -265,25 +261,16 @@ static int smsc47b397_detect(struct i2c_adapter *adapter) ...@@ -265,25 +261,16 @@ static int smsc47b397_detect(struct i2c_adapter *adapter)
goto error_release; goto error_release;
} }
new_client = &data->client; data->addr = res->start;
i2c_set_clientdata(new_client, data); data->name = "smsc47b397";
new_client->addr = address;
mutex_init(&data->lock); mutex_init(&data->lock);
new_client->adapter = adapter;
new_client->driver = &smsc47b397_driver;
new_client->flags = 0;
strlcpy(new_client->name, "smsc47b397", I2C_NAME_SIZE);
mutex_init(&data->update_lock); mutex_init(&data->update_lock);
platform_set_drvdata(pdev, data);
if ((err = i2c_attach_client(new_client))) if ((err = sysfs_create_group(&dev->kobj, &smsc47b397_group)))
goto error_free; goto error_free;
if ((err = sysfs_create_group(&new_client->dev.kobj, &smsc47b397_group))) data->class_dev = hwmon_device_register(dev);
goto error_detach;
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) { if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev); err = PTR_ERR(data->class_dev);
goto error_remove; goto error_remove;
...@@ -292,13 +279,50 @@ static int smsc47b397_detect(struct i2c_adapter *adapter) ...@@ -292,13 +279,50 @@ static int smsc47b397_detect(struct i2c_adapter *adapter)
return 0; return 0;
error_remove: error_remove:
sysfs_remove_group(&new_client->dev.kobj, &smsc47b397_group); sysfs_remove_group(&dev->kobj, &smsc47b397_group);
error_detach:
i2c_detach_client(new_client);
error_free: error_free:
kfree(data); kfree(data);
error_release: error_release:
release_region(address, SMSC_EXTENT); release_region(res->start, SMSC_EXTENT);
return err;
}
static int __init smsc47b397_device_add(unsigned short address)
{
struct resource res = {
.start = address,
.end = address + SMSC_EXTENT - 1,
.name = DRVNAME,
.flags = IORESOURCE_IO,
};
int err;
pdev = platform_device_alloc(DRVNAME, address);
if (!pdev) {
err = -ENOMEM;
printk(KERN_ERR DRVNAME ": Device allocation failed\n");
goto exit;
}
err = platform_device_add_resources(pdev, &res, 1);
if (err) {
printk(KERN_ERR DRVNAME ": Device resource addition failed "
"(%d)\n", err);
goto exit_device_put;
}
err = platform_device_add(pdev);
if (err) {
printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
err);
goto exit_device_put;
}
return 0;
exit_device_put:
platform_device_put(pdev);
exit:
return err; return err;
} }
...@@ -320,7 +344,7 @@ static int __init smsc47b397_find(unsigned short *addr) ...@@ -320,7 +344,7 @@ static int __init smsc47b397_find(unsigned short *addr)
*addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8) *addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8)
| superio_inb(SUPERIO_REG_BASE_LSB); | superio_inb(SUPERIO_REG_BASE_LSB);
printk(KERN_INFO "smsc47b397: found SMSC %s " printk(KERN_INFO DRVNAME ": found SMSC %s "
"(base address 0x%04x, revision %u)\n", "(base address 0x%04x, revision %u)\n",
id == 0x81 ? "SCH5307-NS" : "LPC47B397-NC", *addr, rev); id == 0x81 ? "SCH5307-NS" : "LPC47B397-NC", *addr, rev);
...@@ -330,17 +354,33 @@ static int __init smsc47b397_find(unsigned short *addr) ...@@ -330,17 +354,33 @@ static int __init smsc47b397_find(unsigned short *addr)
static int __init smsc47b397_init(void) static int __init smsc47b397_init(void)
{ {
unsigned short address;
int ret; int ret;
if ((ret = smsc47b397_find(&address))) if ((ret = smsc47b397_find(&address)))
return ret; return ret;
return i2c_isa_add_driver(&smsc47b397_driver); ret = platform_driver_register(&smsc47b397_driver);
if (ret)
goto exit;
/* Sets global pdev as a side effect */
ret = smsc47b397_device_add(address);
if (ret)
goto exit_driver;
return 0;
exit_driver:
platform_driver_unregister(&smsc47b397_driver);
exit:
return ret;
} }
static void __exit smsc47b397_exit(void) static void __exit smsc47b397_exit(void)
{ {
i2c_isa_del_driver(&smsc47b397_driver); platform_device_unregister(pdev);
platform_driver_unregister(&smsc47b397_driver);
} }
MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
......
此差异已折叠。
/* /*
smsc47m192.c - Support for hardware monitoring block of smsc47m192.c - Support for hardware monitoring block of
SMSC LPC47M192 and LPC47M997 Super I/O chips SMSC LPC47M192 and compatible Super I/O chips
Copyright (C) 2006 Hartmut Rick <linux@rick.claranet.de> Copyright (C) 2006 Hartmut Rick <linux@rick.claranet.de>
...@@ -518,7 +518,7 @@ static int smsc47m192_detect(struct i2c_adapter *adapter, int address, ...@@ -518,7 +518,7 @@ static int smsc47m192_detect(struct i2c_adapter *adapter, int address,
&& (i2c_smbus_read_byte_data(client, && (i2c_smbus_read_byte_data(client,
SMSC47M192_REG_VID4) & 0xfe) == 0x80) { SMSC47M192_REG_VID4) & 0xfe) == 0x80) {
dev_info(&adapter->dev, dev_info(&adapter->dev,
"found SMSC47M192 or SMSC47M997, " "found SMSC47M192 or compatible, "
"version 2, stepping A%d\n", version & 0x0f); "version 2, stepping A%d\n", version & 0x0f);
} else { } else {
dev_dbg(&adapter->dev, dev_dbg(&adapter->dev,
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/hwmon-vid.h> #include <linux/hwmon-vid.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/ioport.h>
#include <asm/io.h> #include <asm/io.h>
static int uch_config = -1; static int uch_config = -1;
...@@ -1130,6 +1131,12 @@ static int __devinit vt1211_probe(struct platform_device *pdev) ...@@ -1130,6 +1131,12 @@ static int __devinit vt1211_probe(struct platform_device *pdev)
} }
res = platform_get_resource(pdev, IORESOURCE_IO, 0); res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!request_region(res->start, res->end - res->start + 1, DRVNAME)) {
err = -EBUSY;
dev_err(dev, "Failed to request region 0x%lx-0x%lx\n",
(unsigned long)res->start, (unsigned long)res->end);
goto EXIT_KFREE;
}
data->addr = res->start; data->addr = res->start;
data->name = DRVNAME; data->name = DRVNAME;
mutex_init(&data->update_lock); mutex_init(&data->update_lock);
...@@ -1197,6 +1204,8 @@ static int __devinit vt1211_probe(struct platform_device *pdev) ...@@ -1197,6 +1204,8 @@ static int __devinit vt1211_probe(struct platform_device *pdev)
dev_err(dev, "Sysfs interface creation failed (%d)\n", err); dev_err(dev, "Sysfs interface creation failed (%d)\n", err);
EXIT_DEV_REMOVE_SILENT: EXIT_DEV_REMOVE_SILENT:
vt1211_remove_sysfs(pdev); vt1211_remove_sysfs(pdev);
release_region(res->start, res->end - res->start + 1);
EXIT_KFREE:
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
kfree(data); kfree(data);
EXIT: EXIT:
...@@ -1206,12 +1215,16 @@ static int __devinit vt1211_probe(struct platform_device *pdev) ...@@ -1206,12 +1215,16 @@ static int __devinit vt1211_probe(struct platform_device *pdev)
static int __devexit vt1211_remove(struct platform_device *pdev) static int __devexit vt1211_remove(struct platform_device *pdev)
{ {
struct vt1211_data *data = platform_get_drvdata(pdev); struct vt1211_data *data = platform_get_drvdata(pdev);
struct resource *res;
hwmon_device_unregister(data->class_dev); hwmon_device_unregister(data->class_dev);
vt1211_remove_sysfs(pdev); vt1211_remove_sysfs(pdev);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
kfree(data); kfree(data);
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
release_region(res->start, res->end - res->start + 1);
return 0; return 0;
} }
......
此差异已折叠。
此差异已折叠。
...@@ -77,7 +77,7 @@ static inline unsigned long long native_read_pmc(void) ...@@ -77,7 +77,7 @@ static inline unsigned long long native_read_pmc(void)
#ifdef CONFIG_PARAVIRT #ifdef CONFIG_PARAVIRT
#include <asm/paravirt.h> #include <asm/paravirt.h>
#else #else
#include <linux/errno.h>
/* /*
* Access to machine-specific registers (available on 586 and better only) * Access to machine-specific registers (available on 586 and better only)
* Note: the rd* operations modify the parameters directly (without using * Note: the rd* operations modify the parameters directly (without using
...@@ -148,6 +148,8 @@ static inline void wrmsrl (unsigned long msr, unsigned long long val) ...@@ -148,6 +148,8 @@ static inline void wrmsrl (unsigned long msr, unsigned long long val)
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
void rdmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 *l, u32 *h); void rdmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 *l, u32 *h);
void wrmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 l, u32 h); void wrmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 l, u32 h);
int rdmsr_safe_on_cpu(unsigned int cpu, u32 msr_no, u32 *l, u32 *h);
int wrmsr_safe_on_cpu(unsigned int cpu, u32 msr_no, u32 l, u32 h);
#else /* CONFIG_SMP */ #else /* CONFIG_SMP */
static inline void rdmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 *l, u32 *h) static inline void rdmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 *l, u32 *h)
{ {
...@@ -157,6 +159,14 @@ static inline void wrmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 l, u32 h) ...@@ -157,6 +159,14 @@ static inline void wrmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 l, u32 h)
{ {
wrmsr(msr_no, l, h); wrmsr(msr_no, l, h);
} }
static inline int rdmsr_safe_on_cpu(unsigned int cpu, u32 msr_no, u32 *l, u32 *h)
{
return rdmsr_safe(msr_no, l, h);
}
static inline int wrmsr_safe_on_cpu(unsigned int cpu, u32 msr_no, u32 l, u32 h)
{
return wrmsr_safe(msr_no, l, h);
}
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
#endif #endif
#endif #endif
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册