Commit d310ad0c authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging

* 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging: (22 commits)
  hwmon: (via-cputemp) Remove bogus "SHOW" global variable
  hwmon: jc42 depends on I2C
  hwmon: (pc87427) Add a maintainer
  hwmon: (pc87427) Move sysfs file removal to a separate function
  hwmon: (pc87427) Add temperature monitoring support
  hwmon: (pc87427) Add support for the second logical device
  hwmon: (pc87427) Add support for manual fan speed control
  hwmon: (pc87427) Minor style cleanups
  hwmon: (pc87427) Handle disabled fan inputs properly
  hwmon: (w83627ehf) Add support for W83667HG-B
  hwmon: (w83627ehf) Driver cleanup
  hwmon: Add driver for SMSC EMC2103 temperature monitor and fan controller
  hwmon: Remove in[0-*]_fault from sysfs-interface
  hwmon: Add 4 current alarm/beep attributes to sysfs-interface
  hwmon: Add 3 critical limit attributes to sysfs-interface
  hwmon: (asc7621) Clean up and improve detect function
  hwmon: (it87) Export labels for internal sensors
  hwmon: (lm75) Add suspend/resume feature
  hwmon: (emc1403) Add power support
  hwmon: (ltc4245) Expose all GPIO pins as analog voltages
  ...
parents c29c08b5 f2799418
Kernel driver emc2103
======================
Supported chips:
* SMSC EMC2103
Addresses scanned: I2C 0x2e
Prefix: 'emc2103'
Datasheet: Not public
Authors:
Steve Glendinning <steve.glendinning@smsc.com>
Description
-----------
The Standard Microsystems Corporation (SMSC) EMC2103 chips
contain up to 4 temperature sensors and a single fan controller.
Fan rotation speeds are reported in RPM (rotations per minute). An alarm is
triggered if the rotation speed has dropped below a programmable limit. Fan
readings can be divided by a programmable divider (1, 2, 4 or 8) to give
the readings more range or accuracy. Not all RPM values can accurately be
represented, so some rounding is done. With a divider of 1, the lowest
representable value is 480 RPM.
This driver supports RPM based control, to use this a fan target
should be written to fan1_target and pwm1_enable should be set to 3.
The 2103-2 and 2103-4 variants have a third temperature sensor, which can
be connected to two anti-parallel diodes. These values can be read
as temp3 and temp4. If only one diode is attached to this channel, temp4
will show as "fault". The module parameter "apd=0" can be used to suppress
this 4th channel when anti-parallel diodes are not fitted.
...@@ -72,9 +72,31 @@ in6_min_alarm 5v output undervoltage alarm ...@@ -72,9 +72,31 @@ in6_min_alarm 5v output undervoltage alarm
in7_min_alarm 3v output undervoltage alarm in7_min_alarm 3v output undervoltage alarm
in8_min_alarm Vee (-12v) output undervoltage alarm in8_min_alarm Vee (-12v) output undervoltage alarm
in9_input GPIO voltage data in9_input GPIO voltage data (see note 1)
in10_input GPIO voltage data (see note 1)
in11_input GPIO voltage data (see note 1)
power1_input 12v power usage (mW) power1_input 12v power usage (mW)
power2_input 5v power usage (mW) power2_input 5v power usage (mW)
power3_input 3v power usage (mW) power3_input 3v power usage (mW)
power4_input Vee (-12v) power usage (mW) power4_input Vee (-12v) power usage (mW)
Note 1
------
If you have NOT configured the driver to sample all GPIO pins as analog
voltages, then the in10_input and in11_input sysfs attributes will not be
created. The driver will sample the GPIO pin that is currently connected to the
ADC as an analog voltage, and report the value in in9_input.
If you have configured the driver to sample all GPIO pins as analog voltages,
then they will be sampled in round-robin fashion. If userspace reads too
slowly, -EAGAIN will be returned when you read the sysfs attribute containing
the sensor reading.
The LTC4245 chip can be configured to sample all GPIO pins with two methods:
1) platform data -- see include/linux/i2c/ltc4245.h
2) OF device tree -- add the "ltc4245,use-extra-gpios" property to each chip
The default mode of operation is to sample a single GPIO pin.
...@@ -18,10 +18,11 @@ Description ...@@ -18,10 +18,11 @@ Description
The National Semiconductor Super I/O chip includes complete hardware The National Semiconductor Super I/O chip includes complete hardware
monitoring capabilities. It can monitor up to 18 voltages, 8 fans and monitoring capabilities. It can monitor up to 18 voltages, 8 fans and
6 temperature sensors. Only the fans are supported at the moment. 6 temperature sensors. Only the fans and temperatures are supported at
the moment, voltages aren't.
This chip also has fan controlling features, which are not yet supported This chip also has fan controlling features (up to 4 PWM outputs),
by this driver either. which are partly supported by this driver.
The driver assumes that no more than one chip is present, which seems The driver assumes that no more than one chip is present, which seems
reasonable. reasonable.
...@@ -36,3 +37,23 @@ signal. Speeds down to 83 RPM can be measured. ...@@ -36,3 +37,23 @@ signal. Speeds down to 83 RPM can be measured.
An alarm is triggered if the rotation speed drops below a programmable An alarm is triggered if the rotation speed drops below a programmable
limit. Another alarm is triggered if the speed is too low to be measured limit. Another alarm is triggered if the speed is too low to be measured
(including stalled or missing fan). (including stalled or missing fan).
Fan Speed Control
-----------------
Fan speed can be controlled by PWM outputs. There are 4 possible modes:
always off, always on, manual and automatic. The latter isn't supported
by the driver: you can only return to that mode if it was the original
setting, and the configuration interface is missing.
Temperature Monitoring
----------------------
The PC87427 relies on external sensors (following the SensorPath
standard), so the resolution and range depend on the type of sensor
connected. The integer part can be 8-bit or 9-bit, and can be signed or
not. I couldn't find a way to figure out the external sensor data
temperature format, so user-space adjustment (typically by a factor 2)
may be required.
...@@ -107,10 +107,24 @@ in[0-*]_min Voltage min value. ...@@ -107,10 +107,24 @@ in[0-*]_min Voltage min value.
Unit: millivolt Unit: millivolt
RW RW
in[0-*]_lcrit Voltage critical min value.
Unit: millivolt
RW
If voltage drops to or below this limit, the system may
take drastic action such as power down or reset. At the very
least, it should report a fault.
in[0-*]_max Voltage max value. in[0-*]_max Voltage max value.
Unit: millivolt Unit: millivolt
RW RW
in[0-*]_crit Voltage critical max value.
Unit: millivolt
RW
If voltage reaches or exceeds this limit, the system may
take drastic action such as power down or reset. At the very
least, it should report a fault.
in[0-*]_input Voltage input value. in[0-*]_input Voltage input value.
Unit: millivolt Unit: millivolt
RO RO
...@@ -284,7 +298,7 @@ temp[1-*]_input Temperature input value. ...@@ -284,7 +298,7 @@ temp[1-*]_input Temperature input value.
Unit: millidegree Celsius Unit: millidegree Celsius
RO RO
temp[1-*]_crit Temperature critical value, typically greater than temp[1-*]_crit Temperature critical max value, typically greater than
corresponding temp_max values. corresponding temp_max values.
Unit: millidegree Celsius Unit: millidegree Celsius
RW RW
...@@ -296,6 +310,11 @@ temp[1-*]_crit_hyst ...@@ -296,6 +310,11 @@ temp[1-*]_crit_hyst
from the critical value. from the critical value.
RW RW
temp[1-*]_lcrit Temperature critical min value, typically lower than
corresponding temp_min values.
Unit: millidegree Celsius
RW
temp[1-*]_offset temp[1-*]_offset
Temperature offset which is added to the temperature reading Temperature offset which is added to the temperature reading
by the chip. by the chip.
...@@ -344,9 +363,6 @@ Also see the Alarms section for status flags associated with temperatures. ...@@ -344,9 +363,6 @@ Also see the Alarms section for status flags associated with temperatures.
* Currents * * Currents *
************ ************
Note that no known chip provides current measurements as of writing,
so this part is theoretical, so to say.
curr[1-*]_max Current max value curr[1-*]_max Current max value
Unit: milliampere Unit: milliampere
RW RW
...@@ -471,6 +487,7 @@ limit-related alarms, not both. The driver should just reflect the hardware ...@@ -471,6 +487,7 @@ limit-related alarms, not both. The driver should just reflect the hardware
implementation. implementation.
in[0-*]_alarm in[0-*]_alarm
curr[1-*]_alarm
fan[1-*]_alarm fan[1-*]_alarm
temp[1-*]_alarm temp[1-*]_alarm
Channel alarm Channel alarm
...@@ -482,6 +499,8 @@ OR ...@@ -482,6 +499,8 @@ OR
in[0-*]_min_alarm in[0-*]_min_alarm
in[0-*]_max_alarm in[0-*]_max_alarm
curr[1-*]_min_alarm
curr[1-*]_max_alarm
fan[1-*]_min_alarm fan[1-*]_min_alarm
fan[1-*]_max_alarm fan[1-*]_max_alarm
temp[1-*]_min_alarm temp[1-*]_min_alarm
...@@ -497,7 +516,6 @@ to notify open diodes, unconnected fans etc. where the hardware ...@@ -497,7 +516,6 @@ to notify open diodes, unconnected fans etc. where the hardware
supports it. When this boolean has value 1, the measurement for that supports it. When this boolean has value 1, the measurement for that
channel should not be trusted. channel should not be trusted.
in[0-*]_fault
fan[1-*]_fault fan[1-*]_fault
temp[1-*]_fault temp[1-*]_fault
Input fault condition Input fault condition
...@@ -513,6 +531,7 @@ beep_enable Master beep enable ...@@ -513,6 +531,7 @@ beep_enable Master beep enable
RW RW
in[0-*]_beep in[0-*]_beep
curr[1-*]_beep
fan[1-*]_beep fan[1-*]_beep
temp[1-*]_beep temp[1-*]_beep
Channel beep Channel beep
......
...@@ -20,6 +20,10 @@ Supported chips: ...@@ -20,6 +20,10 @@ Supported chips:
Prefix: 'w83667hg' Prefix: 'w83667hg'
Addresses scanned: ISA address retrieved from Super I/O registers Addresses scanned: ISA address retrieved from Super I/O registers
Datasheet: not available Datasheet: not available
* Winbond W83667HG-B
Prefix: 'w83667hg'
Addresses scanned: ISA address retrieved from Super I/O registers
Datasheet: Available from Nuvoton upon request
Authors: Authors:
Jean Delvare <khali@linux-fr.org> Jean Delvare <khali@linux-fr.org>
...@@ -32,8 +36,8 @@ Description ...@@ -32,8 +36,8 @@ Description
----------- -----------
This driver implements support for the Winbond W83627EHF, W83627EHG, This driver implements support for the Winbond W83627EHF, W83627EHG,
W83627DHG, W83627DHG-P and W83667HG super I/O chips. We will refer to them W83627DHG, W83627DHG-P, W83667HG and W83667HG-B super I/O chips.
collectively as Winbond chips. We will refer to them collectively as Winbond chips.
The chips implement three temperature sensors, five fan rotation The chips implement three temperature sensors, five fan rotation
speed sensors, ten analog voltage sensors (only nine for the 627DHG), one speed sensors, ten analog voltage sensors (only nine for the 627DHG), one
...@@ -68,14 +72,15 @@ follows: ...@@ -68,14 +72,15 @@ follows:
temp1 -> pwm1 temp1 -> pwm1
temp2 -> pwm2 temp2 -> pwm2
temp3 -> pwm3 temp3 -> pwm3
prog -> pwm4 (not on 667HG; the programmable setting is not supported by prog -> pwm4 (not on 667HG and 667HG-B; the programmable setting is not
the driver) supported by the driver)
/sys files /sys files
---------- ----------
name - this is a standard hwmon device entry. For the W83627EHF and W83627EHG, name - this is a standard hwmon device entry. For the W83627EHF and W83627EHG,
it is set to "w83627ehf" and for the W83627DHG it is set to "w83627dhg" it is set to "w83627ehf", for the W83627DHG it is set to "w83627dhg",
and for the W83667HG it is set to "w83667hg".
pwm[1-4] - this file stores PWM duty cycle or DC value (fan speed) in range: pwm[1-4] - this file stores PWM duty cycle or DC value (fan speed) in range:
0 (stop) to 255 (full) 0 (stop) to 255 (full)
......
...@@ -4402,6 +4402,13 @@ M: Jim Cromie <jim.cromie@gmail.com> ...@@ -4402,6 +4402,13 @@ M: Jim Cromie <jim.cromie@gmail.com>
S: Maintained S: Maintained
F: drivers/char/pc8736x_gpio.c F: drivers/char/pc8736x_gpio.c
PC87427 HARDWARE MONITORING DRIVER
M: Jean Delvare <khali@linux-fr.org>
L: lm-sensors@lm-sensors.org
S: Maintained
F: Documentation/hwmon/pc87427
F: drivers/hwmon/pc87427.c
PCA9532 LED DRIVER PCA9532 LED DRIVER
M: Riku Voipio <riku.voipio@iki.fi> M: Riku Voipio <riku.voipio@iki.fi>
S: Maintained S: Maintained
...@@ -5279,6 +5286,13 @@ S: Maintained ...@@ -5279,6 +5286,13 @@ S: Maintained
F: Documentation/hwmon/smm665 F: Documentation/hwmon/smm665
F: drivers/hwmon/smm665.c F: drivers/hwmon/smm665.c
SMSC EMC2103 HARDWARE MONITOR DRIVER
M: Steve Glendinning <steve.glendinning@smsc.com>
L: lm-sensors@lm-sensors.org
S: Supported
F: Documentation/hwmon/emc2103
F: drivers/hwmon/emc2103.c
SMSC47B397 HARDWARE MONITOR DRIVER SMSC47B397 HARDWARE MONITOR DRIVER
M: "Mark M. Hoffman" <mhoffman@lightlink.com> M: "Mark M. Hoffman" <mhoffman@lightlink.com>
L: lm-sensors@lm-sensors.org L: lm-sensors@lm-sensors.org
......
...@@ -465,6 +465,7 @@ config SENSORS_JZ4740 ...@@ -465,6 +465,7 @@ config SENSORS_JZ4740
config SENSORS_JC42 config SENSORS_JC42
tristate "JEDEC JC42.4 compliant temperature sensors" tristate "JEDEC JC42.4 compliant temperature sensors"
depends on I2C
help help
If you say yes here you get support for Jedec JC42.4 compliant If you say yes here you get support for Jedec JC42.4 compliant
temperature sensors. Support will include, but not be limited to, temperature sensors. Support will include, but not be limited to,
...@@ -711,7 +712,8 @@ config SENSORS_PC87427 ...@@ -711,7 +712,8 @@ config SENSORS_PC87427
functions of the National Semiconductor PC87427 Super-I/O chip. functions of the National Semiconductor PC87427 Super-I/O chip.
The chip has two distinct logical devices, one for fan speed The chip has two distinct logical devices, one for fan speed
monitoring and control, and one for voltage and temperature monitoring and control, and one for voltage and temperature
monitoring. Only fan speed monitoring is supported right now. monitoring. Fan speed monitoring and control are supported, as
well as temperature monitoring. Voltages aren't supported yet.
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 pc87427. will be called pc87427.
...@@ -804,6 +806,16 @@ config SENSORS_EMC1403 ...@@ -804,6 +806,16 @@ config SENSORS_EMC1403
Threshold values can be configured using sysfs. Threshold values can be configured using sysfs.
Data from the different diodes are accessible via sysfs. Data from the different diodes are accessible via sysfs.
config SENSORS_EMC2103
tristate "SMSC EMC2103"
depends on I2C
help
If you say yes here you get support for the temperature
and fan sensors of the SMSC EMC2103 chips.
This driver can also be built as a module. If so, the module
will be called emc2103.
config SENSORS_SMSC47M1 config SENSORS_SMSC47M1
tristate "SMSC LPC47M10x and compatibles" tristate "SMSC LPC47M10x and compatibles"
help help
......
...@@ -43,6 +43,7 @@ obj-$(CONFIG_SENSORS_PKGTEMP) += pkgtemp.o ...@@ -43,6 +43,7 @@ obj-$(CONFIG_SENSORS_PKGTEMP) += pkgtemp.o
obj-$(CONFIG_SENSORS_DME1737) += dme1737.o obj-$(CONFIG_SENSORS_DME1737) += dme1737.o
obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o
obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o
obj-$(CONFIG_SENSORS_F71805F) += f71805f.o obj-$(CONFIG_SENSORS_F71805F) += f71805f.o
obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o
obj-$(CONFIG_SENSORS_F75375S) += f75375s.o obj-$(CONFIG_SENSORS_F75375S) += f75375s.o
......
...@@ -1150,9 +1150,6 @@ static int asc7621_detect(struct i2c_client *client, ...@@ -1150,9 +1150,6 @@ static int asc7621_detect(struct i2c_client *client,
{ {
struct i2c_adapter *adapter = client->adapter; struct i2c_adapter *adapter = client->adapter;
int company, verstep, chip_index; int company, verstep, chip_index;
struct device *dev;
dev = &client->dev;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV; return -ENODEV;
...@@ -1169,13 +1166,11 @@ static int asc7621_detect(struct i2c_client *client, ...@@ -1169,13 +1166,11 @@ static int asc7621_detect(struct i2c_client *client,
if (company == asc7621_chips[chip_index].company_id && if (company == asc7621_chips[chip_index].company_id &&
verstep == asc7621_chips[chip_index].verstep_id) { verstep == asc7621_chips[chip_index].verstep_id) {
strlcpy(client->name, asc7621_chips[chip_index].name,
I2C_NAME_SIZE);
strlcpy(info->type, asc7621_chips[chip_index].name, strlcpy(info->type, asc7621_chips[chip_index].name,
I2C_NAME_SIZE); I2C_NAME_SIZE);
dev_info(&adapter->dev, "Matched %s\n", dev_info(&adapter->dev, "Matched %s at 0x%02x\n",
asc7621_chips[chip_index].name); asc7621_chips[chip_index].name, client->addr);
return 0; return 0;
} }
} }
......
...@@ -89,6 +89,35 @@ static ssize_t store_temp(struct device *dev, ...@@ -89,6 +89,35 @@ static ssize_t store_temp(struct device *dev,
return count; return count;
} }
static ssize_t store_bit(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct thermal_data *data = i2c_get_clientdata(client);
struct sensor_device_attribute_2 *sda = to_sensor_dev_attr_2(attr);
unsigned long val;
int retval;
if (strict_strtoul(buf, 10, &val))
return -EINVAL;
mutex_lock(&data->mutex);
retval = i2c_smbus_read_byte_data(client, sda->nr);
if (retval < 0)
goto fail;
retval &= ~sda->index;
if (val)
retval |= sda->index;
retval = i2c_smbus_write_byte_data(client, sda->index, retval);
if (retval == 0)
retval = count;
fail:
mutex_unlock(&data->mutex);
return retval;
}
static ssize_t show_hyst(struct device *dev, static ssize_t show_hyst(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
...@@ -200,6 +229,9 @@ static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO, ...@@ -200,6 +229,9 @@ static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO,
static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO | S_IWUSR,
show_hyst, store_hyst, 0x1A); show_hyst, store_hyst, 0x1A);
static SENSOR_DEVICE_ATTR_2(power_state, S_IRUGO | S_IWUSR,
show_bit, store_bit, 0x03, 0x40);
static struct attribute *mid_att_thermal[] = { static struct attribute *mid_att_thermal[] = {
&sensor_dev_attr_temp1_min.dev_attr.attr, &sensor_dev_attr_temp1_min.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr,
...@@ -225,6 +257,7 @@ static struct attribute *mid_att_thermal[] = { ...@@ -225,6 +257,7 @@ static struct attribute *mid_att_thermal[] = {
&sensor_dev_attr_temp3_max_alarm.dev_attr.attr, &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_crit_hyst.dev_attr.attr, &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr,
&sensor_dev_attr_power_state.dev_attr.attr,
NULL NULL
}; };
......
/*
emc2103.c - Support for SMSC EMC2103
Copyright (c) 2010 SMSC
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>
#include <linux/mutex.h>
/* Addresses scanned */
static const unsigned short normal_i2c[] = { 0x2E, I2C_CLIENT_END };
static const u8 REG_TEMP[4] = { 0x00, 0x02, 0x04, 0x06 };
static const u8 REG_TEMP_MIN[4] = { 0x3c, 0x38, 0x39, 0x3a };
static const u8 REG_TEMP_MAX[4] = { 0x34, 0x30, 0x31, 0x32 };
#define REG_CONF1 0x20
#define REG_TEMP_MAX_ALARM 0x24
#define REG_TEMP_MIN_ALARM 0x25
#define REG_FAN_CONF1 0x42
#define REG_FAN_TARGET_LO 0x4c
#define REG_FAN_TARGET_HI 0x4d
#define REG_FAN_TACH_HI 0x4e
#define REG_FAN_TACH_LO 0x4f
#define REG_PRODUCT_ID 0xfd
#define REG_MFG_ID 0xfe
/* equation 4 from datasheet: rpm = (3932160 * multipler) / count */
#define FAN_RPM_FACTOR 3932160
/* 2103-2 and 2103-4's 3rd temperature sensor can be connected to two diodes
* in anti-parallel mode, and in this configuration both can be read
* independently (so we have 4 temperature inputs). The device can't
* detect if it's connected in this mode, so we have to manually enable
* it. Default is to leave the device in the state it's already in (-1).
* This parameter allows APD mode to be optionally forced on or off */
static int apd = -1;
module_param(apd, bool, 0);
MODULE_PARM_DESC(init, "Set to zero to disable anti-parallel diode mode");
struct temperature {
s8 degrees;
u8 fraction; /* 0-7 multiples of 0.125 */
};
struct emc2103_data {
struct device *hwmon_dev;
struct mutex update_lock;
bool valid; /* registers are valid */
bool fan_rpm_control;
int temp_count; /* num of temp sensors */
unsigned long last_updated; /* in jiffies */
struct temperature temp[4]; /* internal + 3 external */
s8 temp_min[4]; /* no fractional part */
s8 temp_max[4]; /* no fractional part */
u8 temp_min_alarm;
u8 temp_max_alarm;
u8 fan_multiplier;
u16 fan_tach;
u16 fan_target;
};
static int read_u8_from_i2c(struct i2c_client *client, u8 i2c_reg, u8 *output)
{
int status = i2c_smbus_read_byte_data(client, i2c_reg);
if (status < 0) {
dev_warn(&client->dev, "reg 0x%02x, err %d\n",
i2c_reg, status);
} else {
*output = status;
}
return status;
}
static void read_temp_from_i2c(struct i2c_client *client, u8 i2c_reg,
struct temperature *temp)
{
u8 degrees, fractional;
if (read_u8_from_i2c(client, i2c_reg, &degrees) < 0)
return;
if (read_u8_from_i2c(client, i2c_reg + 1, &fractional) < 0)
return;
temp->degrees = degrees;
temp->fraction = (fractional & 0xe0) >> 5;
}
static void read_fan_from_i2c(struct i2c_client *client, u16 *output,
u8 hi_addr, u8 lo_addr)
{
u8 high_byte, lo_byte;
if (read_u8_from_i2c(client, hi_addr, &high_byte) < 0)
return;
if (read_u8_from_i2c(client, lo_addr, &lo_byte) < 0)
return;
*output = ((u16)high_byte << 5) | (lo_byte >> 3);
}
static void write_fan_target_to_i2c(struct i2c_client *client, u16 new_target)
{
u8 high_byte = (new_target & 0x1fe0) >> 5;
u8 low_byte = (new_target & 0x001f) << 3;
i2c_smbus_write_byte_data(client, REG_FAN_TARGET_LO, low_byte);
i2c_smbus_write_byte_data(client, REG_FAN_TARGET_HI, high_byte);
}
static void read_fan_config_from_i2c(struct i2c_client *client)
{
struct emc2103_data *data = i2c_get_clientdata(client);
u8 conf1;
if (read_u8_from_i2c(client, REG_FAN_CONF1, &conf1) < 0)
return;
data->fan_multiplier = 1 << ((conf1 & 0x60) >> 5);
data->fan_rpm_control = (conf1 & 0x80) != 0;
}
static struct emc2103_data *emc2103_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct emc2103_data *data = i2c_get_clientdata(client);
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
int i;
for (i = 0; i < data->temp_count; i++) {
read_temp_from_i2c(client, REG_TEMP[i], &data->temp[i]);
read_u8_from_i2c(client, REG_TEMP_MIN[i],
&data->temp_min[i]);
read_u8_from_i2c(client, REG_TEMP_MAX[i],
&data->temp_max[i]);
}
read_u8_from_i2c(client, REG_TEMP_MIN_ALARM,
&data->temp_min_alarm);
read_u8_from_i2c(client, REG_TEMP_MAX_ALARM,
&data->temp_max_alarm);
read_fan_from_i2c(client, &data->fan_tach,
REG_FAN_TACH_HI, REG_FAN_TACH_LO);
read_fan_from_i2c(client, &data->fan_target,
REG_FAN_TARGET_HI, REG_FAN_TARGET_LO);
read_fan_config_from_i2c(client);
data->last_updated = jiffies;
data->valid = true;
}
mutex_unlock(&data->update_lock);
return data;
}
static ssize_t
show_temp(struct device *dev, struct device_attribute *da, char *buf)
{
int nr = to_sensor_dev_attr(da)->index;
struct emc2103_data *data = emc2103_update_device(dev);
int millidegrees = data->temp[nr].degrees * 1000
+ data->temp[nr].fraction * 125;
return sprintf(buf, "%d\n", millidegrees);
}
static ssize_t
show_temp_min(struct device *dev, struct device_attribute *da, char *buf)
{
int nr = to_sensor_dev_attr(da)->index;
struct emc2103_data *data = emc2103_update_device(dev);
int millidegrees = data->temp_min[nr] * 1000;
return sprintf(buf, "%d\n", millidegrees);
}
static ssize_t
show_temp_max(struct device *dev, struct device_attribute *da, char *buf)
{
int nr = to_sensor_dev_attr(da)->index;
struct emc2103_data *data = emc2103_update_device(dev);
int millidegrees = data->temp_max[nr] * 1000;
return sprintf(buf, "%d\n", millidegrees);
}
static ssize_t
show_temp_fault(struct device *dev, struct device_attribute *da, char *buf)
{
int nr = to_sensor_dev_attr(da)->index;
struct emc2103_data *data = emc2103_update_device(dev);
bool fault = (data->temp[nr].degrees == -128);
return sprintf(buf, "%d\n", fault ? 1 : 0);
}
static ssize_t
show_temp_min_alarm(struct device *dev, struct device_attribute *da, char *buf)
{
int nr = to_sensor_dev_attr(da)->index;
struct emc2103_data *data = emc2103_update_device(dev);
bool alarm = data->temp_min_alarm & (1 << nr);
return sprintf(buf, "%d\n", alarm ? 1 : 0);
}
static ssize_t
show_temp_max_alarm(struct device *dev, struct device_attribute *da, char *buf)
{
int nr = to_sensor_dev_attr(da)->index;
struct emc2103_data *data = emc2103_update_device(dev);
bool alarm = data->temp_max_alarm & (1 << nr);
return sprintf(buf, "%d\n", alarm ? 1 : 0);
}
static ssize_t set_temp_min(struct device *dev, struct device_attribute *da,
const char *buf, size_t count)
{
int nr = to_sensor_dev_attr(da)->index;
struct i2c_client *client = to_i2c_client(dev);
struct emc2103_data *data = i2c_get_clientdata(client);
long val;
int result = strict_strtol(buf, 10, &val);
if (result < 0)
return -EINVAL;
val = DIV_ROUND_CLOSEST(val, 1000);
if ((val < -63) || (val > 127))
return -EINVAL;
mutex_lock(&data->update_lock);
data->temp_min[nr] = val;
i2c_smbus_write_byte_data(client, REG_TEMP_MIN[nr], val);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t set_temp_max(struct device *dev, struct device_attribute *da,
const char *buf, size_t count)
{
int nr = to_sensor_dev_attr(da)->index;
struct i2c_client *client = to_i2c_client(dev);
struct emc2103_data *data = i2c_get_clientdata(client);
long val;
int result = strict_strtol(buf, 10, &val);
if (result < 0)
return -EINVAL;
val = DIV_ROUND_CLOSEST(val, 1000);
if ((val < -63) || (val > 127))
return -EINVAL;
mutex_lock(&data->update_lock);
data->temp_max[nr] = val;
i2c_smbus_write_byte_data(client, REG_TEMP_MAX[nr], val);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t
show_fan(struct device *dev, struct device_attribute *da, char *buf)
{
struct emc2103_data *data = emc2103_update_device(dev);
int rpm = 0;
if (data->fan_tach != 0)
rpm = (FAN_RPM_FACTOR * data->fan_multiplier) / data->fan_tach;
return sprintf(buf, "%d\n", rpm);
}
static ssize_t
show_fan_div(struct device *dev, struct device_attribute *da, char *buf)
{
struct emc2103_data *data = emc2103_update_device(dev);
int fan_div = 8 / data->fan_multiplier;
return sprintf(buf, "%d\n", fan_div);
}
/* Note: we also update the fan target here, because its value is
determined in part by the fan clock divider. This follows the principle
of least surprise; the user doesn't expect the fan target to change just
because the divider changed. */
static ssize_t set_fan_div(struct device *dev, struct device_attribute *da,
const char *buf, size_t count)
{
struct emc2103_data *data = emc2103_update_device(dev);
struct i2c_client *client = to_i2c_client(dev);
int new_range_bits, old_div = 8 / data->fan_multiplier;
long new_div;
int status = strict_strtol(buf, 10, &new_div);
if (status < 0)
return -EINVAL;
if (new_div == old_div) /* No change */
return count;
switch (new_div) {
case 1:
new_range_bits = 3;
break;
case 2:
new_range_bits = 2;
break;
case 4:
new_range_bits = 1;
break;
case 8:
new_range_bits = 0;
break;
default:
return -EINVAL;
}
mutex_lock(&data->update_lock);
status = i2c_smbus_read_byte_data(client, REG_FAN_CONF1);
if (status < 0) {
dev_dbg(&client->dev, "reg 0x%02x, err %d\n",
REG_FAN_CONF1, status);
mutex_unlock(&data->update_lock);
return -EIO;
}
status &= 0x9F;
status |= (new_range_bits << 5);
i2c_smbus_write_byte_data(client, REG_FAN_CONF1, status);
data->fan_multiplier = 8 / new_div;
/* update fan target if high byte is not disabled */
if ((data->fan_target & 0x1fe0) != 0x1fe0) {
u16 new_target = (data->fan_target * old_div) / new_div;
data->fan_target = min(new_target, (u16)0x1fff);
write_fan_target_to_i2c(client, data->fan_target);
}
/* invalidate data to force re-read from hardware */
data->valid = false;
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t
show_fan_target(struct device *dev, struct device_attribute *da, char *buf)
{
struct emc2103_data *data = emc2103_update_device(dev);
int rpm = 0;
/* high byte of 0xff indicates disabled so return 0 */
if ((data->fan_target != 0) && ((data->fan_target & 0x1fe0) != 0x1fe0))
rpm = (FAN_RPM_FACTOR * data->fan_multiplier)
/ data->fan_target;
return sprintf(buf, "%d\n", rpm);
}
static ssize_t set_fan_target(struct device *dev, struct device_attribute *da,
const char *buf, size_t count)
{
struct emc2103_data *data = emc2103_update_device(dev);
struct i2c_client *client = to_i2c_client(dev);
long rpm_target;
int result = strict_strtol(buf, 10, &rpm_target);
if (result < 0)
return -EINVAL;
/* Datasheet states 16384 as maximum RPM target (table 3.2) */
if ((rpm_target < 0) || (rpm_target > 16384))
return -EINVAL;
mutex_lock(&data->update_lock);
if (rpm_target == 0)
data->fan_target = 0x1fff;
else
data->fan_target = SENSORS_LIMIT(
(FAN_RPM_FACTOR * data->fan_multiplier) / rpm_target,
0, 0x1fff);
write_fan_target_to_i2c(client, data->fan_target);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t
show_fan_fault(struct device *dev, struct device_attribute *da, char *buf)
{
struct emc2103_data *data = emc2103_update_device(dev);
bool fault = ((data->fan_tach & 0x1fe0) == 0x1fe0);
return sprintf(buf, "%d\n", fault ? 1 : 0);
}
static ssize_t
show_pwm_enable(struct device *dev, struct device_attribute *da, char *buf)
{
struct emc2103_data *data = emc2103_update_device(dev);
return sprintf(buf, "%d\n", data->fan_rpm_control ? 3 : 0);
}
static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *da,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct emc2103_data *data = i2c_get_clientdata(client);
long new_value;
u8 conf_reg;
int result = strict_strtol(buf, 10, &new_value);
if (result < 0)
return -EINVAL;
mutex_lock(&data->update_lock);
switch (new_value) {
case 0:
data->fan_rpm_control = false;
break;
case 3:
data->fan_rpm_control = true;
break;
default:
mutex_unlock(&data->update_lock);
return -EINVAL;
}
read_u8_from_i2c(client, REG_FAN_CONF1, &conf_reg);
if (data->fan_rpm_control)
conf_reg |= 0x80;
else
conf_reg &= ~0x80;
i2c_smbus_write_byte_data(client, REG_FAN_CONF1, conf_reg);
mutex_unlock(&data->update_lock);
return count;
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR, show_temp_min,
set_temp_min, 0);
static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp_max,
set_temp_max, 0);
static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_temp_min_alarm,
NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_temp_max_alarm,
NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO | S_IWUSR, show_temp_min,
set_temp_min, 1);
static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR, show_temp_max,
set_temp_max, 1);
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1);
static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_temp_min_alarm,
NULL, 1);
static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_temp_max_alarm,
NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO | S_IWUSR, show_temp_min,
set_temp_min, 2);
static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max,
set_temp_max, 2);
static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2);
static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_temp_min_alarm,
NULL, 2);
static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_temp_max_alarm,
NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO | S_IWUSR, show_temp_min,
set_temp_min, 3);
static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO | S_IWUSR, show_temp_max,
set_temp_max, 3);
static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_temp_fault, NULL, 3);
static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_temp_min_alarm,
NULL, 3);
static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_temp_max_alarm,
NULL, 3);
static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL);
static DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, show_fan_div, set_fan_div);
static DEVICE_ATTR(fan1_target, S_IRUGO | S_IWUSR, show_fan_target,
set_fan_target);
static DEVICE_ATTR(fan1_fault, S_IRUGO, show_fan_fault, NULL);
static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable,
set_pwm_enable);
/* sensors present on all models */
static struct attribute *emc2103_attributes[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_min.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp1_fault.dev_attr.attr,
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp2_min.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp2_fault.dev_attr.attr,
&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
&dev_attr_fan1_input.attr,
&dev_attr_fan1_div.attr,
&dev_attr_fan1_target.attr,
&dev_attr_fan1_fault.attr,
&dev_attr_pwm1_enable.attr,
NULL
};
/* extra temperature sensors only present on 2103-2 and 2103-4 */
static struct attribute *emc2103_attributes_temp3[] = {
&sensor_dev_attr_temp3_input.dev_attr.attr,
&sensor_dev_attr_temp3_min.dev_attr.attr,
&sensor_dev_attr_temp3_max.dev_attr.attr,
&sensor_dev_attr_temp3_fault.dev_attr.attr,
&sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
NULL
};
/* extra temperature sensors only present on 2103-2 and 2103-4 in APD mode */
static struct attribute *emc2103_attributes_temp4[] = {
&sensor_dev_attr_temp4_input.dev_attr.attr,
&sensor_dev_attr_temp4_min.dev_attr.attr,
&sensor_dev_attr_temp4_max.dev_attr.attr,
&sensor_dev_attr_temp4_fault.dev_attr.attr,
&sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
NULL
};
static const struct attribute_group emc2103_group = {
.attrs = emc2103_attributes,
};
static const struct attribute_group emc2103_temp3_group = {
.attrs = emc2103_attributes_temp3,
};
static const struct attribute_group emc2103_temp4_group = {
.attrs = emc2103_attributes_temp4,
};
static int
emc2103_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct emc2103_data *data;
int status;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
data = kzalloc(sizeof(struct emc2103_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
/* 2103-2 and 2103-4 have 3 external diodes, 2103-1 has 1 */
status = i2c_smbus_read_byte_data(client, REG_PRODUCT_ID);
if (status == 0x24) {
/* 2103-1 only has 1 external diode */
data->temp_count = 2;
} else {
/* 2103-2 and 2103-4 have 3 or 4 external diodes */
status = i2c_smbus_read_byte_data(client, REG_CONF1);
if (status < 0) {
dev_dbg(&client->dev, "reg 0x%02x, err %d\n", REG_CONF1,
status);
goto exit_free;
}
/* detect current state of hardware */
data->temp_count = (status & 0x01) ? 4 : 3;
/* force APD state if module parameter is set */
if (apd == 0) {
/* force APD mode off */
data->temp_count = 3;
status &= ~(0x01);
i2c_smbus_write_byte_data(client, REG_CONF1, status);
} else if (apd == 1) {
/* force APD mode on */
data->temp_count = 4;
status |= 0x01;
i2c_smbus_write_byte_data(client, REG_CONF1, status);
}
}
/* Register sysfs hooks */
status = sysfs_create_group(&client->dev.kobj, &emc2103_group);
if (status)
goto exit_free;
if (data->temp_count >= 3) {
status = sysfs_create_group(&client->dev.kobj,
&emc2103_temp3_group);
if (status)
goto exit_remove;
}
if (data->temp_count == 4) {
status = sysfs_create_group(&client->dev.kobj,
&emc2103_temp4_group);
if (status)
goto exit_remove_temp3;
}
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
status = PTR_ERR(data->hwmon_dev);
goto exit_remove_temp4;
}
dev_info(&client->dev, "%s: sensor '%s'\n",
dev_name(data->hwmon_dev), client->name);
return 0;
exit_remove_temp4:
if (data->temp_count == 4)
sysfs_remove_group(&client->dev.kobj, &emc2103_temp4_group);
exit_remove_temp3:
if (data->temp_count >= 3)
sysfs_remove_group(&client->dev.kobj, &emc2103_temp3_group);
exit_remove:
sysfs_remove_group(&client->dev.kobj, &emc2103_group);
exit_free:
kfree(data);
return status;
}
static int emc2103_remove(struct i2c_client *client)
{
struct emc2103_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
if (data->temp_count == 4)
sysfs_remove_group(&client->dev.kobj, &emc2103_temp4_group);
if (data->temp_count >= 3)
sysfs_remove_group(&client->dev.kobj, &emc2103_temp3_group);
sysfs_remove_group(&client->dev.kobj, &emc2103_group);
kfree(data);
return 0;
}
static const struct i2c_device_id emc2103_ids[] = {
{ "emc2103", 0, },
{ /* LIST END */ }
};
MODULE_DEVICE_TABLE(i2c, emc2103_ids);
/* Return 0 if detection is successful, -ENODEV otherwise */
static int
emc2103_detect(struct i2c_client *new_client, struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
int manufacturer, product;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
manufacturer = i2c_smbus_read_byte_data(new_client, REG_MFG_ID);
if (manufacturer != 0x5D)
return -ENODEV;
product = i2c_smbus_read_byte_data(new_client, REG_PRODUCT_ID);
if ((product != 0x24) && (product != 0x26))
return -ENODEV;
strlcpy(info->type, "emc2103", I2C_NAME_SIZE);
return 0;
}
static struct i2c_driver emc2103_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "emc2103",
},
.probe = emc2103_probe,
.remove = emc2103_remove,
.id_table = emc2103_ids,
.detect = emc2103_detect,
.address_list = normal_i2c,
};
static int __init sensors_emc2103_init(void)
{
return i2c_add_driver(&emc2103_driver);
}
static void __exit sensors_emc2103_exit(void)
{
i2c_del_driver(&emc2103_driver);
}
MODULE_AUTHOR("Steve Glendinning <steve.glendinning@smsc.com>");
MODULE_DESCRIPTION("SMSC EMC2103 hwmon driver");
MODULE_LICENSE("GPL");
module_init(sensors_emc2103_init);
module_exit(sensors_emc2103_exit);
...@@ -259,6 +259,7 @@ struct it87_sio_data { ...@@ -259,6 +259,7 @@ struct it87_sio_data {
u8 revision; u8 revision;
u8 vid_value; u8 vid_value;
u8 beep_pin; u8 beep_pin;
u8 internal; /* Internal sensors can be labeled */
/* Features skipped based on config or DMI */ /* Features skipped based on config or DMI */
u8 skip_vid; u8 skip_vid;
u8 skip_fan; u8 skip_fan;
...@@ -1194,6 +1195,22 @@ static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, ...@@ -1194,6 +1195,22 @@ static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr,
} }
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
static ssize_t show_label(struct device *dev, struct device_attribute *attr,
char *buf)
{
static const char *labels[] = {
"+5V",
"5VSB",
"Vbat",
};
int nr = to_sensor_dev_attr(attr)->index;
return sprintf(buf, "%s\n", labels[nr]);
}
static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, 0);
static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_label, NULL, 1);
static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_label, NULL, 2);
static ssize_t show_name(struct device *dev, struct device_attribute static ssize_t show_name(struct device *dev, struct device_attribute
*devattr, char *buf) *devattr, char *buf)
{ {
...@@ -1434,6 +1451,17 @@ static const struct attribute_group it87_group_vid = { ...@@ -1434,6 +1451,17 @@ static const struct attribute_group it87_group_vid = {
.attrs = it87_attributes_vid, .attrs = it87_attributes_vid,
}; };
static struct attribute *it87_attributes_label[] = {
&sensor_dev_attr_in3_label.dev_attr.attr,
&sensor_dev_attr_in7_label.dev_attr.attr,
&sensor_dev_attr_in8_label.dev_attr.attr,
NULL
};
static const struct attribute_group it87_group_label = {
.attrs = it87_attributes_vid,
};
/* SuperIO detection - will change isa_address if a chip is found */ /* SuperIO detection - will change isa_address if a chip is found */
static int __init it87_find(unsigned short *address, static int __init it87_find(unsigned short *address,
struct it87_sio_data *sio_data) struct it87_sio_data *sio_data)
...@@ -1487,6 +1515,9 @@ static int __init it87_find(unsigned short *address, ...@@ -1487,6 +1515,9 @@ static int __init it87_find(unsigned short *address,
pr_info("it87: Found IT%04xF chip at 0x%x, revision %d\n", pr_info("it87: Found IT%04xF chip at 0x%x, revision %d\n",
chip_type, *address, sio_data->revision); chip_type, *address, sio_data->revision);
/* in8 (Vbat) is always internal */
sio_data->internal = (1 << 2);
/* Read GPIO config and VID value from LDN 7 (GPIO) */ /* Read GPIO config and VID value from LDN 7 (GPIO) */
if (sio_data->type == it87) { if (sio_data->type == it87) {
/* The IT8705F doesn't have VID pins at all */ /* The IT8705F doesn't have VID pins at all */
...@@ -1540,9 +1571,9 @@ static int __init it87_find(unsigned short *address, ...@@ -1540,9 +1571,9 @@ static int __init it87_find(unsigned short *address,
pr_notice("it87: Routing internal VCCH to in7\n"); pr_notice("it87: Routing internal VCCH to in7\n");
} }
if (reg & (1 << 0)) if (reg & (1 << 0))
pr_info("it87: in3 is VCC (+5V)\n"); sio_data->internal |= (1 << 0);
if (reg & (1 << 1)) if (reg & (1 << 1))
pr_info("it87: in7 is VCCH (+5V Stand-By)\n"); sio_data->internal |= (1 << 1);
sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f;
} }
...@@ -1600,6 +1631,7 @@ static void it87_remove_files(struct device *dev) ...@@ -1600,6 +1631,7 @@ static void it87_remove_files(struct device *dev)
} }
if (!sio_data->skip_vid) if (!sio_data->skip_vid)
sysfs_remove_group(&dev->kobj, &it87_group_vid); sysfs_remove_group(&dev->kobj, &it87_group_vid);
sysfs_remove_group(&dev->kobj, &it87_group_label);
} }
static int __devinit it87_probe(struct platform_device *pdev) static int __devinit it87_probe(struct platform_device *pdev)
...@@ -1725,6 +1757,16 @@ static int __devinit it87_probe(struct platform_device *pdev) ...@@ -1725,6 +1757,16 @@ static int __devinit it87_probe(struct platform_device *pdev)
goto ERROR4; goto ERROR4;
} }
/* Export labels for internal sensors */
for (i = 0; i < 3; i++) {
if (!(sio_data->internal & (1 << i)))
continue;
err = sysfs_create_file(&dev->kobj,
it87_attributes_label[i]);
if (err)
goto ERROR4;
}
data->hwmon_dev = hwmon_device_register(dev); data->hwmon_dev = hwmon_device_register(dev);
if (IS_ERR(data->hwmon_dev)) { if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev); err = PTR_ERR(data->hwmon_dev);
......
...@@ -252,12 +252,13 @@ static int __devinit k8temp_probe(struct pci_dev *pdev, ...@@ -252,12 +252,13 @@ static int __devinit k8temp_probe(struct pci_dev *pdev,
&sensor_dev_attr_temp3_input.dev_attr); &sensor_dev_attr_temp3_input.dev_attr);
if (err) if (err)
goto exit_remove; goto exit_remove;
if (data->sensorsp & SEL_PLACE) if (data->sensorsp & SEL_PLACE) {
err = device_create_file(&pdev->dev, err = device_create_file(&pdev->dev,
&sensor_dev_attr_temp4_input. &sensor_dev_attr_temp4_input.
dev_attr); dev_attr);
if (err) if (err)
goto exit_remove; goto exit_remove;
}
} }
err = device_create_file(&pdev->dev, &dev_attr_name); err = device_create_file(&pdev->dev, &dev_attr_name);
......
...@@ -280,10 +280,49 @@ static int lm75_detect(struct i2c_client *new_client, ...@@ -280,10 +280,49 @@ static int lm75_detect(struct i2c_client *new_client,
return 0; return 0;
} }
#ifdef CONFIG_PM
static int lm75_suspend(struct device *dev)
{
int status;
struct i2c_client *client = to_i2c_client(dev);
status = lm75_read_value(client, LM75_REG_CONF);
if (status < 0) {
dev_dbg(&client->dev, "Can't read config? %d\n", status);
return status;
}
status = status | LM75_SHUTDOWN;
lm75_write_value(client, LM75_REG_CONF, status);
return 0;
}
static int lm75_resume(struct device *dev)
{
int status;
struct i2c_client *client = to_i2c_client(dev);
status = lm75_read_value(client, LM75_REG_CONF);
if (status < 0) {
dev_dbg(&client->dev, "Can't read config? %d\n", status);
return status;
}
status = status & ~LM75_SHUTDOWN;
lm75_write_value(client, LM75_REG_CONF, status);
return 0;
}
static const struct dev_pm_ops lm75_dev_pm_ops = {
.suspend = lm75_suspend,
.resume = lm75_resume,
};
#define LM75_DEV_PM_OPS (&lm75_dev_pm_ops)
#else
#define LM75_DEV_PM_OPS NULL
#endif /* CONFIG_PM */
static struct i2c_driver lm75_driver = { static struct i2c_driver lm75_driver = {
.class = I2C_CLASS_HWMON, .class = I2C_CLASS_HWMON,
.driver = { .driver = {
.name = "lm75", .name = "lm75",
.pm = LM75_DEV_PM_OPS,
}, },
.probe = lm75_probe, .probe = lm75_probe,
.remove = lm75_remove, .remove = lm75_remove,
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
/* straight from the datasheet */ /* straight from the datasheet */
#define LM75_TEMP_MIN (-55000) #define LM75_TEMP_MIN (-55000)
#define LM75_TEMP_MAX 125000 #define LM75_TEMP_MAX 125000
#define LM75_SHUTDOWN 0x01
/* TEMP: 0.001C/bit (-55C to +125C) /* TEMP: 0.001C/bit (-55C to +125C)
REG: (0.5C/bit, two's complement) << 7 */ REG: (0.5C/bit, two's complement) << 7 */
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h> #include <linux/hwmon-sysfs.h>
#include <linux/i2c/ltc4245.h>
/* Here are names of the chip's registers (a.k.a. commands) */ /* Here are names of the chip's registers (a.k.a. commands) */
enum ltc4245_cmd { enum ltc4245_cmd {
...@@ -60,8 +61,72 @@ struct ltc4245_data { ...@@ -60,8 +61,72 @@ struct ltc4245_data {
/* Voltage registers */ /* Voltage registers */
u8 vregs[0x0d]; u8 vregs[0x0d];
/* GPIO ADC registers */
bool use_extra_gpios;
int gpios[3];
}; };
/*
* Update the readings from the GPIO pins. If the driver has been configured to
* sample all GPIO's as analog voltages, a round-robin sampling method is used.
* Otherwise, only the configured GPIO pin is sampled.
*
* LOCKING: must hold data->update_lock
*/
static void ltc4245_update_gpios(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct ltc4245_data *data = i2c_get_clientdata(client);
u8 gpio_curr, gpio_next, gpio_reg;
int i;
/* no extra gpio support, we're basically done */
if (!data->use_extra_gpios) {
data->gpios[0] = data->vregs[LTC4245_GPIOADC - 0x10];
return;
}
/*
* If the last reading was too long ago, then we mark all old GPIO
* readings as stale by setting them to -EAGAIN
*/
if (time_after(jiffies, data->last_updated + 5 * HZ)) {
dev_dbg(&client->dev, "Marking GPIOs invalid\n");
for (i = 0; i < ARRAY_SIZE(data->gpios); i++)
data->gpios[i] = -EAGAIN;
}
/*
* Get the current GPIO pin
*
* The datasheet calls these GPIO[1-3], but we'll calculate the zero
* based array index instead, and call them GPIO[0-2]. This is much
* easier to think about.
*/
gpio_curr = (data->cregs[LTC4245_GPIO] & 0xc0) >> 6;
if (gpio_curr > 0)
gpio_curr -= 1;
/* Read the GPIO voltage from the GPIOADC register */
data->gpios[gpio_curr] = data->vregs[LTC4245_GPIOADC - 0x10];
/* Find the next GPIO pin to read */
gpio_next = (gpio_curr + 1) % ARRAY_SIZE(data->gpios);
/*
* Calculate the correct setting for the GPIO register so it will
* sample the next GPIO pin
*/
gpio_reg = (data->cregs[LTC4245_GPIO] & 0x3f) | ((gpio_next + 1) << 6);
/* Update the GPIO register */
i2c_smbus_write_byte_data(client, LTC4245_GPIO, gpio_reg);
/* Update saved data */
data->cregs[LTC4245_GPIO] = gpio_reg;
}
static struct ltc4245_data *ltc4245_update_device(struct device *dev) static struct ltc4245_data *ltc4245_update_device(struct device *dev)
{ {
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = to_i2c_client(dev);
...@@ -93,6 +158,9 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev) ...@@ -93,6 +158,9 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev)
data->vregs[i] = val; data->vregs[i] = val;
} }
/* Update GPIO readings */
ltc4245_update_gpios(dev);
data->last_updated = jiffies; data->last_updated = jiffies;
data->valid = 1; data->valid = 1;
} }
...@@ -233,6 +301,22 @@ static ssize_t ltc4245_show_alarm(struct device *dev, ...@@ -233,6 +301,22 @@ static ssize_t ltc4245_show_alarm(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0); return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0);
} }
static ssize_t ltc4245_show_gpio(struct device *dev,
struct device_attribute *da,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct ltc4245_data *data = ltc4245_update_device(dev);
int val = data->gpios[attr->index];
/* handle stale GPIO's */
if (val < 0)
return val;
/* Convert to millivolts and print */
return snprintf(buf, PAGE_SIZE, "%u\n", val * 10);
}
/* These macros are used below in constructing device attribute objects /* These macros are used below in constructing device attribute objects
* for use with sysfs_create_group() to make a sysfs device file * for use with sysfs_create_group() to make a sysfs device file
* for each register. * for each register.
...@@ -254,6 +338,10 @@ static ssize_t ltc4245_show_alarm(struct device *dev, ...@@ -254,6 +338,10 @@ static ssize_t ltc4245_show_alarm(struct device *dev,
static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \ static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \
ltc4245_show_alarm, NULL, (mask), reg) ltc4245_show_alarm, NULL, (mask), reg)
#define LTC4245_GPIO_VOLTAGE(name, gpio_num) \
static SENSOR_DEVICE_ATTR(name, S_IRUGO, \
ltc4245_show_gpio, NULL, gpio_num)
/* Construct a sensor_device_attribute structure for each register */ /* Construct a sensor_device_attribute structure for each register */
/* Input voltages */ /* Input voltages */
...@@ -293,7 +381,9 @@ LTC4245_ALARM(in7_min_alarm, (1 << 2), LTC4245_FAULT2); ...@@ -293,7 +381,9 @@ LTC4245_ALARM(in7_min_alarm, (1 << 2), LTC4245_FAULT2);
LTC4245_ALARM(in8_min_alarm, (1 << 3), LTC4245_FAULT2); LTC4245_ALARM(in8_min_alarm, (1 << 3), LTC4245_FAULT2);
/* GPIO voltages */ /* GPIO voltages */
LTC4245_VOLTAGE(in9_input, LTC4245_GPIOADC); LTC4245_GPIO_VOLTAGE(in9_input, 0);
LTC4245_GPIO_VOLTAGE(in10_input, 1);
LTC4245_GPIO_VOLTAGE(in11_input, 2);
/* Power Consumption (virtual) */ /* Power Consumption (virtual) */
LTC4245_POWER(power1_input, LTC4245_12VSENSE); LTC4245_POWER(power1_input, LTC4245_12VSENSE);
...@@ -304,7 +394,7 @@ LTC4245_POWER(power4_input, LTC4245_VEESENSE); ...@@ -304,7 +394,7 @@ LTC4245_POWER(power4_input, LTC4245_VEESENSE);
/* Finally, construct an array of pointers to members of the above objects, /* Finally, construct an array of pointers to members of the above objects,
* as required for sysfs_create_group() * as required for sysfs_create_group()
*/ */
static struct attribute *ltc4245_attributes[] = { static struct attribute *ltc4245_std_attributes[] = {
&sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr, &sensor_dev_attr_in2_input.dev_attr.attr,
&sensor_dev_attr_in3_input.dev_attr.attr, &sensor_dev_attr_in3_input.dev_attr.attr,
...@@ -345,10 +435,77 @@ static struct attribute *ltc4245_attributes[] = { ...@@ -345,10 +435,77 @@ static struct attribute *ltc4245_attributes[] = {
NULL, NULL,
}; };
static const struct attribute_group ltc4245_group = { static struct attribute *ltc4245_gpio_attributes[] = {
.attrs = ltc4245_attributes, &sensor_dev_attr_in10_input.dev_attr.attr,
&sensor_dev_attr_in11_input.dev_attr.attr,
NULL,
};
static const struct attribute_group ltc4245_std_group = {
.attrs = ltc4245_std_attributes,
};
static const struct attribute_group ltc4245_gpio_group = {
.attrs = ltc4245_gpio_attributes,
}; };
static int ltc4245_sysfs_create_groups(struct i2c_client *client)
{
struct ltc4245_data *data = i2c_get_clientdata(client);
struct device *dev = &client->dev;
int ret;
/* register the standard sysfs attributes */
ret = sysfs_create_group(&dev->kobj, &ltc4245_std_group);
if (ret) {
dev_err(dev, "unable to register standard attributes\n");
return ret;
}
/* if we're using the extra gpio support, register it's attributes */
if (data->use_extra_gpios) {
ret = sysfs_create_group(&dev->kobj, &ltc4245_gpio_group);
if (ret) {
dev_err(dev, "unable to register gpio attributes\n");
sysfs_remove_group(&dev->kobj, &ltc4245_std_group);
return ret;
}
}
return 0;
}
static void ltc4245_sysfs_remove_groups(struct i2c_client *client)
{
struct ltc4245_data *data = i2c_get_clientdata(client);
struct device *dev = &client->dev;
if (data->use_extra_gpios)
sysfs_remove_group(&dev->kobj, &ltc4245_gpio_group);
sysfs_remove_group(&dev->kobj, &ltc4245_std_group);
}
static bool ltc4245_use_extra_gpios(struct i2c_client *client)
{
struct ltc4245_platform_data *pdata = dev_get_platdata(&client->dev);
#ifdef CONFIG_OF
struct device_node *np = client->dev.of_node;
#endif
/* prefer platform data */
if (pdata)
return pdata->use_extra_gpios;
#ifdef CONFIG_OF
/* fallback on OF */
if (of_find_property(np, "ltc4245,use-extra-gpios", NULL))
return true;
#endif
return false;
}
static int ltc4245_probe(struct i2c_client *client, static int ltc4245_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
...@@ -367,15 +524,16 @@ static int ltc4245_probe(struct i2c_client *client, ...@@ -367,15 +524,16 @@ static int ltc4245_probe(struct i2c_client *client,
i2c_set_clientdata(client, data); i2c_set_clientdata(client, data);
mutex_init(&data->update_lock); mutex_init(&data->update_lock);
data->use_extra_gpios = ltc4245_use_extra_gpios(client);
/* Initialize the LTC4245 chip */ /* Initialize the LTC4245 chip */
i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00); i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00);
i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00); i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00);
/* Register sysfs hooks */ /* Register sysfs hooks */
ret = sysfs_create_group(&client->dev.kobj, &ltc4245_group); ret = ltc4245_sysfs_create_groups(client);
if (ret) if (ret)
goto out_sysfs_create_group; goto out_sysfs_create_groups;
data->hwmon_dev = hwmon_device_register(&client->dev); data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) { if (IS_ERR(data->hwmon_dev)) {
...@@ -386,8 +544,8 @@ static int ltc4245_probe(struct i2c_client *client, ...@@ -386,8 +544,8 @@ static int ltc4245_probe(struct i2c_client *client,
return 0; return 0;
out_hwmon_device_register: out_hwmon_device_register:
sysfs_remove_group(&client->dev.kobj, &ltc4245_group); ltc4245_sysfs_remove_groups(client);
out_sysfs_create_group: out_sysfs_create_groups:
kfree(data); kfree(data);
out_kzalloc: out_kzalloc:
return ret; return ret;
...@@ -398,8 +556,7 @@ static int ltc4245_remove(struct i2c_client *client) ...@@ -398,8 +556,7 @@ static int ltc4245_remove(struct i2c_client *client)
struct ltc4245_data *data = i2c_get_clientdata(client); struct ltc4245_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev); hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &ltc4245_group); ltc4245_sysfs_remove_groups(client);
kfree(data); kfree(data);
return 0; return 0;
......
...@@ -1610,11 +1610,8 @@ static struct pc87360_data *pc87360_update_device(struct device *dev) ...@@ -1610,11 +1610,8 @@ static struct pc87360_data *pc87360_update_device(struct device *dev)
static int __init pc87360_device_add(unsigned short address) static int __init pc87360_device_add(unsigned short address)
{ {
struct resource res = { struct resource res[3];
.name = "pc87360", int err, i, res_count;
.flags = IORESOURCE_IO,
};
int err, i;
pdev = platform_device_alloc("pc87360", address); pdev = platform_device_alloc("pc87360", address);
if (!pdev) { if (!pdev) {
...@@ -1623,22 +1620,28 @@ static int __init pc87360_device_add(unsigned short address) ...@@ -1623,22 +1620,28 @@ static int __init pc87360_device_add(unsigned short address)
goto exit; goto exit;
} }
memset(res, 0, 3 * sizeof(struct resource));
res_count = 0;
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
if (!extra_isa[i]) if (!extra_isa[i])
continue; continue;
res.start = extra_isa[i]; res[res_count].start = extra_isa[i];
res.end = extra_isa[i] + PC87360_EXTENT - 1; res[res_count].end = extra_isa[i] + PC87360_EXTENT - 1;
res[res_count].name = "pc87360",
res[res_count].flags = IORESOURCE_IO,
err = acpi_check_resource_conflict(&res); err = acpi_check_resource_conflict(&res[res_count]);
if (err) if (err)
goto exit_device_put; goto exit_device_put;
err = platform_device_add_resources(pdev, &res, 1); res_count++;
if (err) { }
printk(KERN_ERR "pc87360: Device resource[%d] "
"addition failed (%d)\n", i, err); err = platform_device_add_resources(pdev, res, res_count);
goto exit_device_put; if (err) {
} printk(KERN_ERR "pc87360: Device resources addition failed "
"(%d)\n", err);
goto exit_device_put;
} }
err = platform_device_add(pdev); err = platform_device_add(pdev);
......
/* /*
* pc87427.c - hardware monitoring driver for the * pc87427.c - hardware monitoring driver for the
* National Semiconductor PC87427 Super-I/O chip * National Semiconductor PC87427 Super-I/O chip
* Copyright (C) 2006 Jean Delvare <khali@linux-fr.org> * Copyright (C) 2006, 2008, 2010 Jean Delvare <khali@linux-fr.org>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
...@@ -15,10 +15,11 @@ ...@@ -15,10 +15,11 @@
* Supports the following chips: * Supports the following chips:
* *
* Chip #vin #fan #pwm #temp devid * Chip #vin #fan #pwm #temp devid
* PC87427 - 8 - - 0xF2 * PC87427 - 8 4 6 0xF2
* *
* This driver assumes that no more than one chip is present. * This driver assumes that no more than one chip is present.
* Only fan inputs are supported so far, although the chip can do much more. * Only fans are fully supported so far. Temperatures are in read-only
* mode, and voltages aren't supported at all.
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -57,6 +58,25 @@ struct pc87427_data { ...@@ -57,6 +58,25 @@ struct pc87427_data {
u16 fan[8]; /* register values */ u16 fan[8]; /* register values */
u16 fan_min[8]; /* register values */ u16 fan_min[8]; /* register values */
u8 fan_status[8]; /* register values */ u8 fan_status[8]; /* register values */
u8 pwm_enabled; /* bit vector */
u8 pwm_auto_ok; /* bit vector */
u8 pwm_enable[4]; /* register values */
u8 pwm[4]; /* register values */
u8 temp_enabled; /* bit vector */
s16 temp[6]; /* register values */
s8 temp_min[6]; /* register values */
s8 temp_max[6]; /* register values */
s8 temp_crit[6]; /* register values */
u8 temp_status[6]; /* register values */
u8 temp_type[6]; /* register values */
};
struct pc87427_sio_data {
unsigned short address[2];
u8 has_fanin;
u8 has_fanout;
}; };
/* /*
...@@ -65,6 +85,13 @@ struct pc87427_data { ...@@ -65,6 +85,13 @@ struct pc87427_data {
#define SIOREG_LDSEL 0x07 /* Logical device select */ #define SIOREG_LDSEL 0x07 /* Logical device select */
#define SIOREG_DEVID 0x20 /* Device ID */ #define SIOREG_DEVID 0x20 /* Device ID */
#define SIOREG_CF2 0x22 /* Configuration 2 */
#define SIOREG_CF3 0x23 /* Configuration 3 */
#define SIOREG_CF4 0x24 /* Configuration 4 */
#define SIOREG_CF5 0x25 /* Configuration 5 */
#define SIOREG_CFB 0x2B /* Configuration B */
#define SIOREG_CFC 0x2C /* Configuration C */
#define SIOREG_CFD 0x2D /* Configuration D */
#define SIOREG_ACT 0x30 /* Device activation */ #define SIOREG_ACT 0x30 /* Device activation */
#define SIOREG_MAP 0x50 /* I/O or memory mapping */ #define SIOREG_MAP 0x50 /* I/O or memory mapping */
#define SIOREG_IOBASE 0x60 /* I/O base address */ #define SIOREG_IOBASE 0x60 /* I/O base address */
...@@ -102,6 +129,8 @@ static inline void superio_exit(int sioaddr) ...@@ -102,6 +129,8 @@ static inline void superio_exit(int sioaddr)
#define BANK_FM(nr) (nr) #define BANK_FM(nr) (nr)
#define BANK_FT(nr) (0x08 + (nr)) #define BANK_FT(nr) (0x08 + (nr))
#define BANK_FC(nr) (0x10 + (nr) * 2) #define BANK_FC(nr) (0x10 + (nr) * 2)
#define BANK_TM(nr) (nr)
#define BANK_VM(nr) (0x08 + (nr))
/* /*
* I/O access functions * I/O access functions
...@@ -178,6 +207,127 @@ static inline u16 fan_to_reg(unsigned long val) ...@@ -178,6 +207,127 @@ static inline u16 fan_to_reg(unsigned long val)
return ((1350000UL + val / 2) / val) << 2; return ((1350000UL + val / 2) / val) << 2;
} }
/*
* PWM registers and conversions
*/
#define PC87427_REG_PWM_ENABLE 0x10
#define PC87427_REG_PWM_DUTY 0x12
#define PWM_ENABLE_MODE_MASK (7 << 4)
#define PWM_ENABLE_CTLEN (1 << 0)
#define PWM_MODE_MANUAL (0 << 4)
#define PWM_MODE_AUTO (1 << 4)
#define PWM_MODE_OFF (2 << 4)
#define PWM_MODE_ON (7 << 4)
/* Dedicated function to read all registers related to a given PWM output.
This saves us quite a few locks and bank selections.
Must be called with data->lock held.
nr is from 0 to 3 */
static void pc87427_readall_pwm(struct pc87427_data *data, u8 nr)
{
int iobase = data->address[LD_FAN];
outb(BANK_FC(nr), iobase + PC87427_REG_BANK);
data->pwm_enable[nr] = inb(iobase + PC87427_REG_PWM_ENABLE);
data->pwm[nr] = inb(iobase + PC87427_REG_PWM_DUTY);
}
static inline int pwm_enable_from_reg(u8 reg)
{
switch (reg & PWM_ENABLE_MODE_MASK) {
case PWM_MODE_ON:
return 0;
case PWM_MODE_MANUAL:
case PWM_MODE_OFF:
return 1;
case PWM_MODE_AUTO:
return 2;
default:
return -EPROTO;
}
}
static inline u8 pwm_enable_to_reg(unsigned long val, u8 pwmval)
{
switch (val) {
default:
return PWM_MODE_ON;
case 1:
return pwmval ? PWM_MODE_MANUAL : PWM_MODE_OFF;
case 2:
return PWM_MODE_AUTO;
}
}
/*
* Temperature registers and conversions
*/
#define PC87427_REG_TEMP_STATUS 0x10
#define PC87427_REG_TEMP 0x14
#define PC87427_REG_TEMP_MAX 0x18
#define PC87427_REG_TEMP_MIN 0x19
#define PC87427_REG_TEMP_CRIT 0x1a
#define PC87427_REG_TEMP_TYPE 0x1d
#define TEMP_STATUS_CHANEN (1 << 0)
#define TEMP_STATUS_LOWFLG (1 << 1)
#define TEMP_STATUS_HIGHFLG (1 << 2)
#define TEMP_STATUS_CRITFLG (1 << 3)
#define TEMP_STATUS_SENSERR (1 << 5)
#define TEMP_TYPE_MASK (3 << 5)
#define TEMP_TYPE_THERMISTOR (1 << 5)
#define TEMP_TYPE_REMOTE_DIODE (2 << 5)
#define TEMP_TYPE_LOCAL_DIODE (3 << 5)
/* Dedicated function to read all registers related to a given temperature
input. This saves us quite a few locks and bank selections.
Must be called with data->lock held.
nr is from 0 to 5 */
static void pc87427_readall_temp(struct pc87427_data *data, u8 nr)
{
int iobase = data->address[LD_TEMP];
outb(BANK_TM(nr), iobase + PC87427_REG_BANK);
data->temp[nr] = le16_to_cpu(inw(iobase + PC87427_REG_TEMP));
data->temp_max[nr] = inb(iobase + PC87427_REG_TEMP_MAX);
data->temp_min[nr] = inb(iobase + PC87427_REG_TEMP_MIN);
data->temp_crit[nr] = inb(iobase + PC87427_REG_TEMP_CRIT);
data->temp_type[nr] = inb(iobase + PC87427_REG_TEMP_TYPE);
data->temp_status[nr] = inb(iobase + PC87427_REG_TEMP_STATUS);
/* Clear fan alarm bits */
outb(data->temp_status[nr], iobase + PC87427_REG_TEMP_STATUS);
}
static inline unsigned int temp_type_from_reg(u8 reg)
{
switch (reg & TEMP_TYPE_MASK) {
case TEMP_TYPE_THERMISTOR:
return 4;
case TEMP_TYPE_REMOTE_DIODE:
case TEMP_TYPE_LOCAL_DIODE:
return 3;
default:
return 0;
}
}
/* We assume 8-bit thermal sensors; 9-bit thermal sensors are possible
too, but I have no idea how to figure out when they are used. */
static inline long temp_from_reg(s16 reg)
{
return reg * 1000 / 256;
}
static inline long temp_from_reg8(s8 reg)
{
return reg * 1000;
}
/* /*
* Data interface * Data interface
*/ */
...@@ -198,6 +348,21 @@ static struct pc87427_data *pc87427_update_device(struct device *dev) ...@@ -198,6 +348,21 @@ static struct pc87427_data *pc87427_update_device(struct device *dev)
continue; continue;
pc87427_readall_fan(data, i); pc87427_readall_fan(data, i);
} }
/* PWM outputs */
for (i = 0; i < 4; i++) {
if (!(data->pwm_enabled & (1 << i)))
continue;
pc87427_readall_pwm(data, i);
}
/* Temperature channels */
for (i = 0; i < 6; i++) {
if (!(data->temp_enabled & (1 << i)))
continue;
pc87427_readall_temp(data, i);
}
data->last_updated = jiffies; data->last_updated = jiffies;
done: done:
...@@ -208,9 +373,8 @@ static struct pc87427_data *pc87427_update_device(struct device *dev) ...@@ -208,9 +373,8 @@ static struct pc87427_data *pc87427_update_device(struct device *dev)
static ssize_t show_fan_input(struct device *dev, struct device_attribute static ssize_t show_fan_input(struct device *dev, struct device_attribute
*devattr, char *buf) *devattr, char *buf)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct pc87427_data *data = pc87427_update_device(dev); struct pc87427_data *data = pc87427_update_device(dev);
int nr = attr->index; int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%lu\n", fan_from_reg(data->fan[nr])); return sprintf(buf, "%lu\n", fan_from_reg(data->fan[nr]));
} }
...@@ -218,9 +382,8 @@ static ssize_t show_fan_input(struct device *dev, struct device_attribute ...@@ -218,9 +382,8 @@ static ssize_t show_fan_input(struct device *dev, struct device_attribute
static ssize_t show_fan_min(struct device *dev, struct device_attribute static ssize_t show_fan_min(struct device *dev, struct device_attribute
*devattr, char *buf) *devattr, char *buf)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct pc87427_data *data = pc87427_update_device(dev); struct pc87427_data *data = pc87427_update_device(dev);
int nr = attr->index; int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%lu\n", fan_from_reg(data->fan_min[nr])); return sprintf(buf, "%lu\n", fan_from_reg(data->fan_min[nr]));
} }
...@@ -228,9 +391,8 @@ static ssize_t show_fan_min(struct device *dev, struct device_attribute ...@@ -228,9 +391,8 @@ static ssize_t show_fan_min(struct device *dev, struct device_attribute
static ssize_t show_fan_alarm(struct device *dev, struct device_attribute static ssize_t show_fan_alarm(struct device *dev, struct device_attribute
*devattr, char *buf) *devattr, char *buf)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct pc87427_data *data = pc87427_update_device(dev); struct pc87427_data *data = pc87427_update_device(dev);
int nr = attr->index; int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%d\n", !!(data->fan_status[nr] return sprintf(buf, "%d\n", !!(data->fan_status[nr]
& FAN_STATUS_LOSPD)); & FAN_STATUS_LOSPD));
...@@ -239,9 +401,8 @@ static ssize_t show_fan_alarm(struct device *dev, struct device_attribute ...@@ -239,9 +401,8 @@ static ssize_t show_fan_alarm(struct device *dev, struct device_attribute
static ssize_t show_fan_fault(struct device *dev, struct device_attribute static ssize_t show_fan_fault(struct device *dev, struct device_attribute
*devattr, char *buf) *devattr, char *buf)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct pc87427_data *data = pc87427_update_device(dev); struct pc87427_data *data = pc87427_update_device(dev);
int nr = attr->index; int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%d\n", !!(data->fan_status[nr] return sprintf(buf, "%d\n", !!(data->fan_status[nr]
& FAN_STATUS_STALL)); & FAN_STATUS_STALL));
...@@ -251,11 +412,13 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute ...@@ -251,11 +412,13 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count) *devattr, const char *buf, size_t count)
{ {
struct pc87427_data *data = dev_get_drvdata(dev); struct pc87427_data *data = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); int nr = to_sensor_dev_attr(devattr)->index;
int nr = attr->index; unsigned long val;
unsigned long val = simple_strtoul(buf, NULL, 10);
int iobase = data->address[LD_FAN]; int iobase = data->address[LD_FAN];
if (strict_strtoul(buf, 10, &val) < 0)
return -EINVAL;
mutex_lock(&data->lock); mutex_lock(&data->lock);
outb(BANK_FM(nr), iobase + PC87427_REG_BANK); outb(BANK_FM(nr), iobase + PC87427_REG_BANK);
/* The low speed limit registers are read-only while monitoring /* The low speed limit registers are read-only while monitoring
...@@ -377,6 +540,390 @@ static const struct attribute_group pc87427_group_fan[8] = { ...@@ -377,6 +540,390 @@ static const struct attribute_group pc87427_group_fan[8] = {
{ .attrs = pc87427_attributes_fan[7] }, { .attrs = pc87427_attributes_fan[7] },
}; };
/* Must be called with data->lock held and pc87427_readall_pwm() freshly
called */
static void update_pwm_enable(struct pc87427_data *data, int nr, u8 mode)
{
int iobase = data->address[LD_FAN];
data->pwm_enable[nr] &= ~PWM_ENABLE_MODE_MASK;
data->pwm_enable[nr] |= mode;
outb(data->pwm_enable[nr], iobase + PC87427_REG_PWM_ENABLE);
}
static ssize_t show_pwm_enable(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
int pwm_enable;
pwm_enable = pwm_enable_from_reg(data->pwm_enable[nr]);
if (pwm_enable < 0)
return pwm_enable;
return sprintf(buf, "%d\n", pwm_enable);
}
static ssize_t set_pwm_enable(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
struct pc87427_data *data = dev_get_drvdata(dev);
int nr = to_sensor_dev_attr(devattr)->index;
unsigned long val;
if (strict_strtoul(buf, 10, &val) < 0 || val > 2)
return -EINVAL;
/* Can't go to automatic mode if it isn't configured */
if (val == 2 && !(data->pwm_auto_ok & (1 << nr)))
return -EINVAL;
mutex_lock(&data->lock);
pc87427_readall_pwm(data, nr);
update_pwm_enable(data, nr, pwm_enable_to_reg(val, data->pwm[nr]));
mutex_unlock(&data->lock);
return count;
}
static ssize_t show_pwm(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%d\n", (int)data->pwm[nr]);
}
static ssize_t set_pwm(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
struct pc87427_data *data = dev_get_drvdata(dev);
int nr = to_sensor_dev_attr(devattr)->index;
unsigned long val;
int iobase = data->address[LD_FAN];
u8 mode;
if (strict_strtoul(buf, 10, &val) < 0 || val > 0xff)
return -EINVAL;
mutex_lock(&data->lock);
pc87427_readall_pwm(data, nr);
mode = data->pwm_enable[nr] & PWM_ENABLE_MODE_MASK;
if (mode != PWM_MODE_MANUAL && mode != PWM_MODE_OFF) {
dev_notice(dev, "Can't set PWM%d duty cycle while not in "
"manual mode\n", nr + 1);
mutex_unlock(&data->lock);
return -EPERM;
}
/* We may have to change the mode */
if (mode == PWM_MODE_MANUAL && val == 0) {
/* Transition from Manual to Off */
update_pwm_enable(data, nr, PWM_MODE_OFF);
mode = PWM_MODE_OFF;
dev_dbg(dev, "Switching PWM%d from %s to %s\n", nr + 1,
"manual", "off");
} else if (mode == PWM_MODE_OFF && val != 0) {
/* Transition from Off to Manual */
update_pwm_enable(data, nr, PWM_MODE_MANUAL);
mode = PWM_MODE_MANUAL;
dev_dbg(dev, "Switching PWM%d from %s to %s\n", nr + 1,
"off", "manual");
}
data->pwm[nr] = val;
if (mode == PWM_MODE_MANUAL)
outb(val, iobase + PC87427_REG_PWM_DUTY);
mutex_unlock(&data->lock);
return count;
}
static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
show_pwm_enable, set_pwm_enable, 0);
static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO,
show_pwm_enable, set_pwm_enable, 1);
static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO,
show_pwm_enable, set_pwm_enable, 2);
static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO,
show_pwm_enable, set_pwm_enable, 3);
static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0);
static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1);
static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2);
static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3);
static struct attribute *pc87427_attributes_pwm[4][3] = {
{
&sensor_dev_attr_pwm1_enable.dev_attr.attr,
&sensor_dev_attr_pwm1.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_pwm2_enable.dev_attr.attr,
&sensor_dev_attr_pwm2.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_pwm3_enable.dev_attr.attr,
&sensor_dev_attr_pwm3.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_pwm4_enable.dev_attr.attr,
&sensor_dev_attr_pwm4.dev_attr.attr,
NULL
}
};
static const struct attribute_group pc87427_group_pwm[4] = {
{ .attrs = pc87427_attributes_pwm[0] },
{ .attrs = pc87427_attributes_pwm[1] },
{ .attrs = pc87427_attributes_pwm[2] },
{ .attrs = pc87427_attributes_pwm[3] },
};
static ssize_t show_temp_input(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%ld\n", temp_from_reg(data->temp[nr]));
}
static ssize_t show_temp_min(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%ld\n", temp_from_reg8(data->temp_min[nr]));
}
static ssize_t show_temp_max(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%ld\n", temp_from_reg8(data->temp_max[nr]));
}
static ssize_t show_temp_crit(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%ld\n", temp_from_reg8(data->temp_crit[nr]));
}
static ssize_t show_temp_type(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%u\n", temp_type_from_reg(data->temp_type[nr]));
}
static ssize_t show_temp_min_alarm(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%d\n", !!(data->temp_status[nr]
& TEMP_STATUS_LOWFLG));
}
static ssize_t show_temp_max_alarm(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%d\n", !!(data->temp_status[nr]
& TEMP_STATUS_HIGHFLG));
}
static ssize_t show_temp_crit_alarm(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%d\n", !!(data->temp_status[nr]
& TEMP_STATUS_CRITFLG));
}
static ssize_t show_temp_fault(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct pc87427_data *data = pc87427_update_device(dev);
int nr = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%d\n", !!(data->temp_status[nr]
& TEMP_STATUS_SENSERR));
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp_input, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp_input, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp_input, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_temp_input, NULL, 5);
static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp_min, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp_min, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO, show_temp_min, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_min, S_IRUGO, show_temp_min, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_min, S_IRUGO, show_temp_min, NULL, 5);
static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp_max, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp_max, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO, show_temp_max, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_max, S_IRUGO, show_temp_max, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_max, S_IRUGO, show_temp_max, NULL, 5);
static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp_crit, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_crit, S_IRUGO, show_temp_crit, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_crit, S_IRUGO, show_temp_crit, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_crit, S_IRUGO, show_temp_crit, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_crit, S_IRUGO, show_temp_crit, NULL, 5);
static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_type, S_IRUGO, show_temp_type, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_type, S_IRUGO, show_temp_type, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_type, S_IRUGO, show_temp_type, NULL, 5);
static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO,
show_temp_min_alarm, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO,
show_temp_min_alarm, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO,
show_temp_min_alarm, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO,
show_temp_min_alarm, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_min_alarm, S_IRUGO,
show_temp_min_alarm, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_min_alarm, S_IRUGO,
show_temp_min_alarm, NULL, 5);
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
show_temp_max_alarm, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO,
show_temp_max_alarm, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO,
show_temp_max_alarm, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO,
show_temp_max_alarm, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO,
show_temp_max_alarm, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_max_alarm, S_IRUGO,
show_temp_max_alarm, NULL, 5);
static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
show_temp_crit_alarm, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO,
show_temp_crit_alarm, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO,
show_temp_crit_alarm, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_crit_alarm, S_IRUGO,
show_temp_crit_alarm, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_crit_alarm, S_IRUGO,
show_temp_crit_alarm, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_crit_alarm, S_IRUGO,
show_temp_crit_alarm, NULL, 5);
static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_temp_fault, NULL, 3);
static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_temp_fault, NULL, 4);
static SENSOR_DEVICE_ATTR(temp6_fault, S_IRUGO, show_temp_fault, NULL, 5);
static struct attribute *pc87427_attributes_temp[6][10] = {
{
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_min.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp1_crit.dev_attr.attr,
&sensor_dev_attr_temp1_type.dev_attr.attr,
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_fault.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp2_min.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp2_crit.dev_attr.attr,
&sensor_dev_attr_temp2_type.dev_attr.attr,
&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_fault.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_temp3_input.dev_attr.attr,
&sensor_dev_attr_temp3_min.dev_attr.attr,
&sensor_dev_attr_temp3_max.dev_attr.attr,
&sensor_dev_attr_temp3_crit.dev_attr.attr,
&sensor_dev_attr_temp3_type.dev_attr.attr,
&sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_fault.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_temp4_input.dev_attr.attr,
&sensor_dev_attr_temp4_min.dev_attr.attr,
&sensor_dev_attr_temp4_max.dev_attr.attr,
&sensor_dev_attr_temp4_crit.dev_attr.attr,
&sensor_dev_attr_temp4_type.dev_attr.attr,
&sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp4_fault.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_temp5_input.dev_attr.attr,
&sensor_dev_attr_temp5_min.dev_attr.attr,
&sensor_dev_attr_temp5_max.dev_attr.attr,
&sensor_dev_attr_temp5_crit.dev_attr.attr,
&sensor_dev_attr_temp5_type.dev_attr.attr,
&sensor_dev_attr_temp5_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp5_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp5_fault.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_temp6_input.dev_attr.attr,
&sensor_dev_attr_temp6_min.dev_attr.attr,
&sensor_dev_attr_temp6_max.dev_attr.attr,
&sensor_dev_attr_temp6_crit.dev_attr.attr,
&sensor_dev_attr_temp6_type.dev_attr.attr,
&sensor_dev_attr_temp6_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp6_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp6_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp6_fault.dev_attr.attr,
NULL
}
};
static const struct attribute_group pc87427_group_temp[6] = {
{ .attrs = pc87427_attributes_temp[0] },
{ .attrs = pc87427_attributes_temp[1] },
{ .attrs = pc87427_attributes_temp[2] },
{ .attrs = pc87427_attributes_temp[3] },
{ .attrs = pc87427_attributes_temp[4] },
{ .attrs = pc87427_attributes_temp[5] },
};
static ssize_t show_name(struct device *dev, struct device_attribute static ssize_t show_name(struct device *dev, struct device_attribute
*devattr, char *buf) *devattr, char *buf)
{ {
...@@ -391,8 +938,49 @@ static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); ...@@ -391,8 +938,49 @@ static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
* Device detection, attach and detach * Device detection, attach and detach
*/ */
static void pc87427_release_regions(struct platform_device *pdev, int count)
{
struct resource *res;
int i;
for (i = 0; i < count; i++) {
res = platform_get_resource(pdev, IORESOURCE_IO, i);
release_region(res->start, resource_size(res));
}
}
static int __devinit pc87427_request_regions(struct platform_device *pdev,
int count)
{
struct resource *res;
int i, err = 0;
for (i = 0; i < count; i++) {
res = platform_get_resource(pdev, IORESOURCE_IO, i);
if (!res) {
err = -ENOENT;
dev_err(&pdev->dev, "Missing resource #%d\n", i);
break;
}
if (!request_region(res->start, resource_size(res), DRVNAME)) {
err = -EBUSY;
dev_err(&pdev->dev,
"Failed to request region 0x%lx-0x%lx\n",
(unsigned long)res->start,
(unsigned long)res->end);
break;
}
}
if (err && i)
pc87427_release_regions(pdev, i);
return err;
}
static void __devinit pc87427_init_device(struct device *dev) static void __devinit pc87427_init_device(struct device *dev)
{ {
struct pc87427_sio_data *sio_data = dev->platform_data;
struct pc87427_data *data = dev_get_drvdata(dev); struct pc87427_data *data = dev_get_drvdata(dev);
int i; int i;
u8 reg; u8 reg;
...@@ -400,10 +988,12 @@ static void __devinit pc87427_init_device(struct device *dev) ...@@ -400,10 +988,12 @@ static void __devinit pc87427_init_device(struct device *dev)
/* The FMC module should be ready */ /* The FMC module should be ready */
reg = pc87427_read8(data, LD_FAN, PC87427_REG_BANK); reg = pc87427_read8(data, LD_FAN, PC87427_REG_BANK);
if (!(reg & 0x80)) if (!(reg & 0x80))
dev_warn(dev, "FMC module not ready!\n"); dev_warn(dev, "%s module not ready!\n", "FMC");
/* Check which fans are enabled */ /* Check which fans are enabled */
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
if (!(sio_data->has_fanin & (1 << i))) /* Not wired */
continue;
reg = pc87427_read8_bank(data, LD_FAN, BANK_FM(i), reg = pc87427_read8_bank(data, LD_FAN, BANK_FM(i),
PC87427_REG_FAN_STATUS); PC87427_REG_FAN_STATUS);
if (reg & FAN_STATUS_MONEN) if (reg & FAN_STATUS_MONEN)
...@@ -411,37 +1001,93 @@ static void __devinit pc87427_init_device(struct device *dev) ...@@ -411,37 +1001,93 @@ static void __devinit pc87427_init_device(struct device *dev)
} }
if (!data->fan_enabled) { if (!data->fan_enabled) {
dev_dbg(dev, "Enabling all fan inputs\n"); dev_dbg(dev, "Enabling monitoring of all fans\n");
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++) {
if (!(sio_data->has_fanin & (1 << i))) /* Not wired */
continue;
pc87427_write8_bank(data, LD_FAN, BANK_FM(i), pc87427_write8_bank(data, LD_FAN, BANK_FM(i),
PC87427_REG_FAN_STATUS, PC87427_REG_FAN_STATUS,
FAN_STATUS_MONEN); FAN_STATUS_MONEN);
data->fan_enabled = 0xff; }
data->fan_enabled = sio_data->has_fanin;
}
/* Check which PWM outputs are enabled */
for (i = 0; i < 4; i++) {
if (!(sio_data->has_fanout & (1 << i))) /* Not wired */
continue;
reg = pc87427_read8_bank(data, LD_FAN, BANK_FC(i),
PC87427_REG_PWM_ENABLE);
if (reg & PWM_ENABLE_CTLEN)
data->pwm_enabled |= (1 << i);
/* We don't expose an interface to reconfigure the automatic
fan control mode, so only allow to return to this mode if
it was originally set. */
if ((reg & PWM_ENABLE_MODE_MASK) == PWM_MODE_AUTO) {
dev_dbg(dev, "PWM%d is in automatic control mode\n",
i + 1);
data->pwm_auto_ok |= (1 << i);
}
}
/* The HMC module should be ready */
reg = pc87427_read8(data, LD_TEMP, PC87427_REG_BANK);
if (!(reg & 0x80))
dev_warn(dev, "%s module not ready!\n", "HMC");
/* Check which temperature channels are enabled */
for (i = 0; i < 6; i++) {
reg = pc87427_read8_bank(data, LD_TEMP, BANK_TM(i),
PC87427_REG_TEMP_STATUS);
if (reg & TEMP_STATUS_CHANEN)
data->temp_enabled |= (1 << i);
}
}
static void pc87427_remove_files(struct device *dev)
{
struct pc87427_data *data = dev_get_drvdata(dev);
int i;
device_remove_file(dev, &dev_attr_name);
for (i = 0; i < 8; i++) {
if (!(data->fan_enabled & (1 << i)))
continue;
sysfs_remove_group(&dev->kobj, &pc87427_group_fan[i]);
}
for (i = 0; i < 4; i++) {
if (!(data->pwm_enabled & (1 << i)))
continue;
sysfs_remove_group(&dev->kobj, &pc87427_group_pwm[i]);
}
for (i = 0; i < 6; i++) {
if (!(data->temp_enabled & (1 << i)))
continue;
sysfs_remove_group(&dev->kobj, &pc87427_group_temp[i]);
} }
} }
static int __devinit pc87427_probe(struct platform_device *pdev) static int __devinit pc87427_probe(struct platform_device *pdev)
{ {
struct pc87427_sio_data *sio_data = pdev->dev.platform_data;
struct pc87427_data *data; struct pc87427_data *data;
struct resource *res; int i, err, res_count;
int i, err;
if (!(data = kzalloc(sizeof(struct pc87427_data), GFP_KERNEL))) { data = kzalloc(sizeof(struct pc87427_data), GFP_KERNEL);
if (!data) {
err = -ENOMEM; err = -ENOMEM;
printk(KERN_ERR DRVNAME ": Out of memory\n"); printk(KERN_ERR DRVNAME ": Out of memory\n");
goto exit; goto exit;
} }
/* This will need to be revisited when we add support for data->address[0] = sio_data->address[0];
temperature and voltage monitoring. */ data->address[1] = sio_data->address[1];
res = platform_get_resource(pdev, IORESOURCE_IO, 0); res_count = (data->address[0] != 0) + (data->address[1] != 0);
if (!request_region(res->start, resource_size(res), DRVNAME)) {
err = -EBUSY; err = pc87427_request_regions(pdev, res_count);
dev_err(&pdev->dev, "Failed to request region 0x%lx-0x%lx\n", if (err)
(unsigned long)res->start, (unsigned long)res->end);
goto exit_kfree; goto exit_kfree;
}
data->address[0] = res->start;
mutex_init(&data->lock); mutex_init(&data->lock);
data->name = "pc87427"; data->name = "pc87427";
...@@ -449,13 +1095,31 @@ static int __devinit pc87427_probe(struct platform_device *pdev) ...@@ -449,13 +1095,31 @@ static int __devinit pc87427_probe(struct platform_device *pdev)
pc87427_init_device(&pdev->dev); pc87427_init_device(&pdev->dev);
/* Register sysfs hooks */ /* Register sysfs hooks */
if ((err = device_create_file(&pdev->dev, &dev_attr_name))) err = device_create_file(&pdev->dev, &dev_attr_name);
if (err)
goto exit_release_region; 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;
if ((err = sysfs_create_group(&pdev->dev.kobj, err = sysfs_create_group(&pdev->dev.kobj,
&pc87427_group_fan[i]))) &pc87427_group_fan[i]);
if (err)
goto exit_remove_files;
}
for (i = 0; i < 4; i++) {
if (!(data->pwm_enabled & (1 << i)))
continue;
err = sysfs_create_group(&pdev->dev.kobj,
&pc87427_group_pwm[i]);
if (err)
goto exit_remove_files;
}
for (i = 0; i < 6; i++) {
if (!(data->temp_enabled & (1 << i)))
continue;
err = sysfs_create_group(&pdev->dev.kobj,
&pc87427_group_temp[i]);
if (err)
goto exit_remove_files; goto exit_remove_files;
} }
...@@ -469,13 +1133,9 @@ static int __devinit pc87427_probe(struct platform_device *pdev) ...@@ -469,13 +1133,9 @@ static int __devinit pc87427_probe(struct platform_device *pdev)
return 0; return 0;
exit_remove_files: exit_remove_files:
for (i = 0; i < 8; i++) { pc87427_remove_files(&pdev->dev);
if (!(data->fan_enabled & (1 << i)))
continue;
sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]);
}
exit_release_region: exit_release_region:
release_region(res->start, resource_size(res)); pc87427_release_regions(pdev, res_count);
exit_kfree: exit_kfree:
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
kfree(data); kfree(data);
...@@ -486,21 +1146,16 @@ static int __devinit pc87427_probe(struct platform_device *pdev) ...@@ -486,21 +1146,16 @@ 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 res_count;
int i;
res_count = (data->address[0] != 0) + (data->address[1] != 0);
hwmon_device_unregister(data->hwmon_dev); hwmon_device_unregister(data->hwmon_dev);
device_remove_file(&pdev->dev, &dev_attr_name); pc87427_remove_files(&pdev->dev);
for (i = 0; i < 8; i++) {
if (!(data->fan_enabled & (1 << i)))
continue;
sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]);
}
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
kfree(data); kfree(data);
res = platform_get_resource(pdev, IORESOURCE_IO, 0); pc87427_release_regions(pdev, res_count);
release_region(res->start, resource_size(res));
return 0; return 0;
} }
...@@ -515,34 +1170,50 @@ static struct platform_driver pc87427_driver = { ...@@ -515,34 +1170,50 @@ static struct platform_driver pc87427_driver = {
.remove = __devexit_p(pc87427_remove), .remove = __devexit_p(pc87427_remove),
}; };
static int __init pc87427_device_add(unsigned short address) static int __init pc87427_device_add(const struct pc87427_sio_data *sio_data)
{ {
struct resource res = { struct resource res[2] = {
.start = address, { .flags = IORESOURCE_IO },
.end = address + REGION_LENGTH - 1, { .flags = IORESOURCE_IO },
.name = logdev_str[0],
.flags = IORESOURCE_IO,
}; };
int err; int err, i, res_count;
err = acpi_check_resource_conflict(&res); res_count = 0;
if (err) for (i = 0; i < 2; i++) {
goto exit; if (!sio_data->address[i])
continue;
res[res_count].start = sio_data->address[i];
res[res_count].end = sio_data->address[i] + REGION_LENGTH - 1;
res[res_count].name = logdev_str[i];
pdev = platform_device_alloc(DRVNAME, address); err = acpi_check_resource_conflict(&res[res_count]);
if (err)
goto exit;
res_count++;
}
pdev = platform_device_alloc(DRVNAME, res[0].start);
if (!pdev) { if (!pdev) {
err = -ENOMEM; err = -ENOMEM;
printk(KERN_ERR DRVNAME ": Device allocation failed\n"); printk(KERN_ERR DRVNAME ": Device allocation failed\n");
goto exit; goto exit;
} }
err = platform_device_add_resources(pdev, &res, 1); err = platform_device_add_resources(pdev, res, res_count);
if (err) { if (err) {
printk(KERN_ERR DRVNAME ": Device resource addition failed " printk(KERN_ERR DRVNAME ": Device resource addition failed "
"(%d)\n", err); "(%d)\n", err);
goto exit_device_put; goto exit_device_put;
} }
err = platform_device_add_data(pdev, sio_data,
sizeof(struct pc87427_sio_data));
if (err) {
printk(KERN_ERR DRVNAME ": Platform data allocation failed\n");
goto exit_device_put;
}
err = platform_device_add(pdev); err = platform_device_add(pdev);
if (err) { if (err) {
printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
...@@ -558,9 +1229,10 @@ static int __init pc87427_device_add(unsigned short address) ...@@ -558,9 +1229,10 @@ static int __init pc87427_device_add(unsigned short address)
return err; return err;
} }
static int __init pc87427_find(int sioaddr, unsigned short *address) static int __init pc87427_find(int sioaddr, struct pc87427_sio_data *sio_data)
{ {
u16 val; u16 val;
u8 cfg, cfg_b;
int i, err = 0; int i, err = 0;
/* Identify device */ /* Identify device */
...@@ -571,7 +1243,7 @@ static int __init pc87427_find(int sioaddr, unsigned short *address) ...@@ -571,7 +1243,7 @@ static int __init pc87427_find(int sioaddr, unsigned short *address)
} }
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
address[i] = 0; sio_data->address[i] = 0;
/* Select logical device */ /* Select logical device */
superio_outb(sioaddr, SIOREG_LDSEL, logdev[i]); superio_outb(sioaddr, SIOREG_LDSEL, logdev[i]);
...@@ -596,9 +1268,58 @@ static int __init pc87427_find(int sioaddr, unsigned short *address) ...@@ -596,9 +1268,58 @@ static int __init pc87427_find(int sioaddr, unsigned short *address)
"for logical device 0x%02x\n", logdev[i]); "for logical device 0x%02x\n", logdev[i]);
continue; continue;
} }
address[i] = val; sio_data->address[i] = val;
} }
/* No point in loading the driver if everything is disabled */
if (!sio_data->address[0] && !sio_data->address[1]) {
err = -ENODEV;
goto exit;
}
/* Check which fan inputs are wired */
sio_data->has_fanin = (1 << 2) | (1 << 3); /* FANIN2, FANIN3 */
cfg = superio_inb(sioaddr, SIOREG_CF2);
if (!(cfg & (1 << 3)))
sio_data->has_fanin |= (1 << 0); /* FANIN0 */
if (!(cfg & (1 << 2)))
sio_data->has_fanin |= (1 << 4); /* FANIN4 */
cfg = superio_inb(sioaddr, SIOREG_CFD);
if (!(cfg & (1 << 0)))
sio_data->has_fanin |= (1 << 1); /* FANIN1 */
cfg = superio_inb(sioaddr, SIOREG_CF4);
if (!(cfg & (1 << 0)))
sio_data->has_fanin |= (1 << 7); /* FANIN7 */
cfg_b = superio_inb(sioaddr, SIOREG_CFB);
if (!(cfg & (1 << 1)) && (cfg_b & (1 << 3)))
sio_data->has_fanin |= (1 << 5); /* FANIN5 */
cfg = superio_inb(sioaddr, SIOREG_CF3);
if ((cfg & (1 << 3)) && !(cfg_b & (1 << 5)))
sio_data->has_fanin |= (1 << 6); /* FANIN6 */
/* Check which fan outputs are wired */
sio_data->has_fanout = (1 << 0); /* FANOUT0 */
if (cfg_b & (1 << 0))
sio_data->has_fanout |= (1 << 3); /* FANOUT3 */
cfg = superio_inb(sioaddr, SIOREG_CFC);
if (!(cfg & (1 << 4))) {
if (cfg_b & (1 << 1))
sio_data->has_fanout |= (1 << 1); /* FANOUT1 */
if (cfg_b & (1 << 2))
sio_data->has_fanout |= (1 << 2); /* FANOUT2 */
}
/* FANOUT1 and FANOUT2 can each be routed to 2 different pins */
cfg = superio_inb(sioaddr, SIOREG_CF5);
if (cfg & (1 << 6))
sio_data->has_fanout |= (1 << 1); /* FANOUT1 */
if (cfg & (1 << 5))
sio_data->has_fanout |= (1 << 2); /* FANOUT2 */
exit: exit:
superio_exit(sioaddr); superio_exit(sioaddr);
return err; return err;
...@@ -607,15 +1328,10 @@ static int __init pc87427_find(int sioaddr, unsigned short *address) ...@@ -607,15 +1328,10 @@ static int __init pc87427_find(int sioaddr, unsigned short *address)
static int __init pc87427_init(void) static int __init pc87427_init(void)
{ {
int err; int err;
unsigned short address[2]; struct pc87427_sio_data sio_data;
if (pc87427_find(0x2e, address)
&& pc87427_find(0x4e, address))
return -ENODEV;
/* For now the driver only handles fans so we only care about the if (pc87427_find(0x2e, &sio_data)
first address. */ && pc87427_find(0x4e, &sio_data))
if (!address[0])
return -ENODEV; return -ENODEV;
err = platform_driver_register(&pc87427_driver); err = platform_driver_register(&pc87427_driver);
...@@ -623,7 +1339,7 @@ static int __init pc87427_init(void) ...@@ -623,7 +1339,7 @@ static int __init pc87427_init(void)
goto exit; goto exit;
/* Sets global pdev as a side effect */ /* Sets global pdev as a side effect */
err = pc87427_device_add(address[0]); err = pc87427_device_add(&sio_data);
if (err) if (err)
goto exit_driver; goto exit_driver;
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
#define DRVNAME "via_cputemp" #define DRVNAME "via_cputemp"
enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME } SHOW; enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME };
/* /*
* Functions declaration * Functions declaration
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3 w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3
w83627dhg-p 9 5 4 3 0xb070 0xc1 0x5ca3 w83627dhg-p 9 5 4 3 0xb070 0xc1 0x5ca3
w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3 w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3
w83667hg-b 9 5 3 3 0xb350 0xc1 0x5ca3
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -55,7 +56,7 @@ ...@@ -55,7 +56,7 @@
#include <linux/io.h> #include <linux/io.h>
#include "lm75.h" #include "lm75.h"
enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg }; enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg, w83667hg_b };
/* used to set data->name = w83627ehf_device_names[data->sio_kind] */ /* used to set data->name = w83627ehf_device_names[data->sio_kind] */
static const char * w83627ehf_device_names[] = { static const char * w83627ehf_device_names[] = {
...@@ -63,6 +64,7 @@ static const char * w83627ehf_device_names[] = { ...@@ -63,6 +64,7 @@ static const char * w83627ehf_device_names[] = {
"w83627dhg", "w83627dhg",
"w83627dhg", "w83627dhg",
"w83667hg", "w83667hg",
"w83667hg",
}; };
static unsigned short force_id; static unsigned short force_id;
...@@ -91,6 +93,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID"); ...@@ -91,6 +93,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID");
#define SIO_W83627DHG_ID 0xa020 #define SIO_W83627DHG_ID 0xa020
#define SIO_W83627DHG_P_ID 0xb070 #define SIO_W83627DHG_P_ID 0xb070
#define SIO_W83667HG_ID 0xa510 #define SIO_W83667HG_ID 0xa510
#define SIO_W83667HG_B_ID 0xb350
#define SIO_ID_MASK 0xFFF0 #define SIO_ID_MASK 0xFFF0
static inline void static inline void
...@@ -201,8 +204,14 @@ static const u8 W83627EHF_REG_TOLERANCE[] = { 0x07, 0x07, 0x14, 0x62 }; ...@@ -201,8 +204,14 @@ static const u8 W83627EHF_REG_TOLERANCE[] = { 0x07, 0x07, 0x14, 0x62 };
static const u8 W83627EHF_REG_FAN_START_OUTPUT[] = { 0x0a, 0x0b, 0x16, 0x65 }; static const u8 W83627EHF_REG_FAN_START_OUTPUT[] = { 0x0a, 0x0b, 0x16, 0x65 };
static const u8 W83627EHF_REG_FAN_STOP_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 }; static const u8 W83627EHF_REG_FAN_STOP_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 };
static const u8 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0c, 0x0d, 0x17, 0x66 }; static const u8 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0c, 0x0d, 0x17, 0x66 };
static const u8 W83627EHF_REG_FAN_MAX_OUTPUT[] = { 0xff, 0x67, 0xff, 0x69 };
static const u8 W83627EHF_REG_FAN_STEP_OUTPUT[] = { 0xff, 0x68, 0xff, 0x6a }; static const u8 W83627EHF_REG_FAN_MAX_OUTPUT_COMMON[]
= { 0xff, 0x67, 0xff, 0x69 };
static const u8 W83627EHF_REG_FAN_STEP_OUTPUT_COMMON[]
= { 0xff, 0x68, 0xff, 0x6a };
static const u8 W83627EHF_REG_FAN_MAX_OUTPUT_W83667_B[] = { 0x67, 0x69, 0x6b };
static const u8 W83627EHF_REG_FAN_STEP_OUTPUT_W83667_B[] = { 0x68, 0x6a, 0x6c };
/* /*
* Conversions * Conversions
...@@ -277,6 +286,11 @@ struct w83627ehf_data { ...@@ -277,6 +286,11 @@ struct w83627ehf_data {
struct device *hwmon_dev; struct device *hwmon_dev;
struct mutex lock; struct mutex lock;
const u8 *REG_FAN_START_OUTPUT;
const u8 *REG_FAN_STOP_OUTPUT;
const u8 *REG_FAN_MAX_OUTPUT;
const u8 *REG_FAN_STEP_OUTPUT;
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 */
...@@ -524,7 +538,10 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) ...@@ -524,7 +538,10 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
} }
} }
for (i = 0; i < 4; i++) { for (i = 0; i < data->pwm_num; i++) {
if (!(data->has_fan & (1 << i)))
continue;
/* pwmcfg, tolerance mapped for i=0, i=1 to same reg */ /* pwmcfg, tolerance mapped for i=0, i=1 to same reg */
if (i != 1) { if (i != 1) {
pwmcfg = w83627ehf_read_value(data, pwmcfg = w83627ehf_read_value(data,
...@@ -546,6 +563,17 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) ...@@ -546,6 +563,17 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
W83627EHF_REG_FAN_STOP_OUTPUT[i]); W83627EHF_REG_FAN_STOP_OUTPUT[i]);
data->fan_stop_time[i] = w83627ehf_read_value(data, data->fan_stop_time[i] = w83627ehf_read_value(data,
W83627EHF_REG_FAN_STOP_TIME[i]); W83627EHF_REG_FAN_STOP_TIME[i]);
if (data->REG_FAN_MAX_OUTPUT[i] != 0xff)
data->fan_max_output[i] =
w83627ehf_read_value(data,
data->REG_FAN_MAX_OUTPUT[i]);
if (data->REG_FAN_STEP_OUTPUT[i] != 0xff)
data->fan_step_output[i] =
w83627ehf_read_value(data,
data->REG_FAN_STEP_OUTPUT[i]);
data->target_temp[i] = data->target_temp[i] =
w83627ehf_read_value(data, w83627ehf_read_value(data,
W83627EHF_REG_TARGET[i]) & W83627EHF_REG_TARGET[i]) &
...@@ -1126,7 +1154,7 @@ store_##reg(struct device *dev, struct device_attribute *attr, \ ...@@ -1126,7 +1154,7 @@ store_##reg(struct device *dev, struct device_attribute *attr, \
u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 1, 255); \ u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 1, 255); \
mutex_lock(&data->update_lock); \ mutex_lock(&data->update_lock); \
data->reg[nr] = val; \ data->reg[nr] = val; \
w83627ehf_write_value(data, W83627EHF_REG_##REG[nr], val); \ w83627ehf_write_value(data, data->REG_##REG[nr], val); \
mutex_unlock(&data->update_lock); \ mutex_unlock(&data->update_lock); \
return count; \ return count; \
} }
...@@ -1206,12 +1234,26 @@ static struct sensor_device_attribute sda_sf3_arrays[] = { ...@@ -1206,12 +1234,26 @@ static struct sensor_device_attribute sda_sf3_arrays[] = {
store_fan_stop_output, 1), store_fan_stop_output, 1),
SENSOR_ATTR(pwm3_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output, SENSOR_ATTR(pwm3_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output,
store_fan_stop_output, 2), store_fan_stop_output, 2),
};
/* pwm1 and pwm3 don't support max and step settings */
/*
* pwm1 and pwm3 don't support max and step settings on all chips.
* Need to check support while generating/removing attribute files.
*/
static struct sensor_device_attribute sda_sf3_max_step_arrays[] = {
SENSOR_ATTR(pwm1_max_output, S_IWUSR | S_IRUGO, show_fan_max_output,
store_fan_max_output, 0),
SENSOR_ATTR(pwm1_step_output, S_IWUSR | S_IRUGO, show_fan_step_output,
store_fan_step_output, 0),
SENSOR_ATTR(pwm2_max_output, S_IWUSR | S_IRUGO, show_fan_max_output, SENSOR_ATTR(pwm2_max_output, S_IWUSR | S_IRUGO, show_fan_max_output,
store_fan_max_output, 1), store_fan_max_output, 1),
SENSOR_ATTR(pwm2_step_output, S_IWUSR | S_IRUGO, show_fan_step_output, SENSOR_ATTR(pwm2_step_output, S_IWUSR | S_IRUGO, show_fan_step_output,
store_fan_step_output, 1), store_fan_step_output, 1),
SENSOR_ATTR(pwm3_max_output, S_IWUSR | S_IRUGO, show_fan_max_output,
store_fan_max_output, 2),
SENSOR_ATTR(pwm3_step_output, S_IWUSR | S_IRUGO, show_fan_step_output,
store_fan_step_output, 2),
}; };
static ssize_t static ssize_t
...@@ -1235,6 +1277,12 @@ static void w83627ehf_device_remove_files(struct device *dev) ...@@ -1235,6 +1277,12 @@ static void w83627ehf_device_remove_files(struct device *dev)
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++) for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++)
device_remove_file(dev, &sda_sf3_arrays[i].dev_attr); device_remove_file(dev, &sda_sf3_arrays[i].dev_attr);
for (i = 0; i < ARRAY_SIZE(sda_sf3_max_step_arrays); i++) {
struct sensor_device_attribute *attr =
&sda_sf3_max_step_arrays[i];
if (data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff)
device_remove_file(dev, &attr->dev_attr);
}
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr); device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
for (i = 0; i < data->in_num; i++) { for (i = 0; i < data->in_num; i++) {
...@@ -1343,22 +1391,37 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) ...@@ -1343,22 +1391,37 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
/* 627EHG and 627EHF have 10 voltage inputs; 627DHG and 667HG have 9 */ /* 627EHG and 627EHF have 10 voltage inputs; 627DHG and 667HG have 9 */
data->in_num = (sio_data->kind == w83627ehf) ? 10 : 9; data->in_num = (sio_data->kind == w83627ehf) ? 10 : 9;
/* 667HG has 3 pwms */ /* 667HG has 3 pwms */
data->pwm_num = (sio_data->kind == w83667hg) ? 3 : 4; data->pwm_num = (sio_data->kind == w83667hg
|| sio_data->kind == w83667hg_b) ? 3 : 4;
/* Check temp3 configuration bit for 667HG */ /* Check temp3 configuration bit for 667HG */
if (sio_data->kind == w83667hg) { if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) {
data->temp3_disable = w83627ehf_read_value(data, data->temp3_disable = w83627ehf_read_value(data,
W83627EHF_REG_TEMP_CONFIG[1]) & 0x01; W83627EHF_REG_TEMP_CONFIG[1]) & 0x01;
data->in6_skip = !data->temp3_disable; data->in6_skip = !data->temp3_disable;
} }
data->REG_FAN_START_OUTPUT = W83627EHF_REG_FAN_START_OUTPUT;
data->REG_FAN_STOP_OUTPUT = W83627EHF_REG_FAN_STOP_OUTPUT;
if (sio_data->kind == w83667hg_b) {
data->REG_FAN_MAX_OUTPUT =
W83627EHF_REG_FAN_MAX_OUTPUT_W83667_B;
data->REG_FAN_STEP_OUTPUT =
W83627EHF_REG_FAN_STEP_OUTPUT_W83667_B;
} else {
data->REG_FAN_MAX_OUTPUT =
W83627EHF_REG_FAN_MAX_OUTPUT_COMMON;
data->REG_FAN_STEP_OUTPUT =
W83627EHF_REG_FAN_STEP_OUTPUT_COMMON;
}
/* Initialize the chip */ /* Initialize the chip */
w83627ehf_init_device(data); w83627ehf_init_device(data);
data->vrm = vid_which_vrm(); data->vrm = vid_which_vrm();
superio_enter(sio_data->sioreg); superio_enter(sio_data->sioreg);
/* Read VID value */ /* Read VID value */
if (sio_data->kind == w83667hg) { if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) {
/* W83667HG has different pins for VID input and output, so /* W83667HG has different pins for VID input and output, so
we can get the VID input values directly at logical device D we can get the VID input values directly at logical device D
0xe3. */ 0xe3. */
...@@ -1409,7 +1472,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) ...@@ -1409,7 +1472,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
} }
/* fan4 and fan5 share some pins with the GPIO and serial flash */ /* fan4 and fan5 share some pins with the GPIO and serial flash */
if (sio_data->kind == w83667hg) { if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) {
fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20; fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20;
fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40; fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40;
} else { } else {
...@@ -1440,6 +1503,15 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) ...@@ -1440,6 +1503,15 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
&sda_sf3_arrays[i].dev_attr))) &sda_sf3_arrays[i].dev_attr)))
goto exit_remove; goto exit_remove;
for (i = 0; i < ARRAY_SIZE(sda_sf3_max_step_arrays); i++) {
struct sensor_device_attribute *attr =
&sda_sf3_max_step_arrays[i];
if (data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff) {
err = device_create_file(dev, &attr->dev_attr);
if (err)
goto exit_remove;
}
}
/* if fan4 is enabled create the sf3 files for it */ /* if fan4 is enabled create the sf3 files for it */
if ((data->has_fan & (1 << 3)) && data->pwm_num >= 4) if ((data->has_fan & (1 << 3)) && data->pwm_num >= 4)
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) { for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) {
...@@ -1556,6 +1628,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, ...@@ -1556,6 +1628,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
static const char __initdata sio_name_W83627DHG[] = "W83627DHG"; static const char __initdata sio_name_W83627DHG[] = "W83627DHG";
static const char __initdata sio_name_W83627DHG_P[] = "W83627DHG-P"; static const char __initdata sio_name_W83627DHG_P[] = "W83627DHG-P";
static const char __initdata sio_name_W83667HG[] = "W83667HG"; static const char __initdata sio_name_W83667HG[] = "W83667HG";
static const char __initdata sio_name_W83667HG_B[] = "W83667HG-B";
u16 val; u16 val;
const char *sio_name; const char *sio_name;
...@@ -1588,6 +1661,10 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, ...@@ -1588,6 +1661,10 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
sio_data->kind = w83667hg; sio_data->kind = w83667hg;
sio_name = sio_name_W83667HG; sio_name = sio_name_W83667HG;
break; break;
case SIO_W83667HG_B_ID:
sio_data->kind = w83667hg_b;
sio_name = sio_name_W83667HG_B;
break;
default: default:
if (val != 0xffff) if (val != 0xffff)
pr_debug(DRVNAME ": unsupported chip ID: 0x%04x\n", pr_debug(DRVNAME ": unsupported chip ID: 0x%04x\n",
......
/*
* Platform Data for LTC4245 hardware monitor chip
*
* Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
*
* 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.
*/
#ifndef LINUX_LTC4245_H
#define LINUX_LTC4245_H
#include <linux/types.h>
struct ltc4245_platform_data {
bool use_extra_gpios;
};
#endif /* LINUX_LTC4245_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment