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

* 'hwmon-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6:
  hwmon/vt1211: Add probing of alternate config index port
  hwmon/f71805f: Fix a race condition
  hwmon/abituguru: Fix unchecked return status
  hwmon: New driver for the Analog Devices ADM1029
  hwmon/w83627ehf: Add support for the W83627DHG chip
  hwmon: Use subsys_initcall
  hwmon/lm70: Make lm70_remove a __devexit function
  hwmon: Cleanup a bogus legacy comment
  hwmon: Simplify the locking model of two drivers
  hwmon: Drop unused mutexes in two drivers
  hwmon/it87: Add PWM base frequency control
This commit is contained in:
Linus Torvalds 2007-02-19 13:36:16 -08:00
commit 42eaf0d8f2
21 changed files with 769 additions and 137 deletions

View File

@ -135,6 +135,16 @@ Give 0 for unused sensor. Any other value is invalid. To configure this at
startup, consult lm_sensors's /etc/sensors.conf. (2 = thermistor; startup, consult lm_sensors's /etc/sensors.conf. (2 = thermistor;
3 = thermal diode) 3 = thermal diode)
Fan speed control
-----------------
The fan speed control features are limited to manual PWM mode. Automatic The fan speed control features are limited to manual PWM mode. Automatic
"Smart Guardian" mode control handling is not implemented. However "Smart Guardian" mode control handling is not implemented. However
if you want to go for "manual mode" just write 1 to pwmN_enable. if you want to go for "manual mode" just write 1 to pwmN_enable.
If you are only able to control the fan speed with very small PWM values,
try lowering the PWM base frequency (pwm1_freq). Depending on the fan,
it may give you a somewhat greater control range. The same frequency is
used to drive all fan outputs, which is why pwm2_freq and pwm3_freq are
read-only.

View File

@ -166,16 +166,21 @@ pwm[1-*] Pulse width modulation fan control.
pwm[1-*]_enable pwm[1-*]_enable
Switch PWM on and off. Switch PWM on and off.
Not always present even if fan*_pwm is. Not always present even if pwmN is.
0: turn off 0: turn off
1: turn on in manual mode 1: turn on in manual mode
2+: turn on in automatic mode 2+: turn on in automatic mode
Check individual chip documentation files for automatic mode details. Check individual chip documentation files for automatic mode
details.
RW RW
pwm[1-*]_mode pwm[1-*]_mode 0: DC mode (direct current)
0: DC mode 1: PWM mode (pulse-width modulation)
1: PWM mode RW
pwm[1-*]_freq Base PWM frequency in Hz.
Only possibly available when pwmN_mode is PWM, but not always
present even then.
RW RW
pwm[1-*]_auto_channels_temp pwm[1-*]_auto_channels_temp

View File

@ -2,26 +2,29 @@ Kernel driver w83627ehf
======================= =======================
Supported chips: Supported chips:
* Winbond W83627EHF/EHG (ISA access ONLY) * Winbond W83627EHF/EHG/DHG (ISA access ONLY)
Prefix: 'w83627ehf' Prefix: 'w83627ehf'
Addresses scanned: ISA address retrieved from Super I/O registers Addresses scanned: ISA address retrieved from Super I/O registers
Datasheet: http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf Datasheet:
http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf
DHG datasheet confidential.
Authors: Authors:
Jean Delvare <khali@linux-fr.org> Jean Delvare <khali@linux-fr.org>
Yuan Mu (Winbond) Yuan Mu (Winbond)
Rudolf Marek <r.marek@assembler.cz> Rudolf Marek <r.marek@assembler.cz>
David Hubbard <david.c.hubbard@gmail.com>
Description Description
----------- -----------
This driver implements support for the Winbond W83627EHF and W83627EHG This driver implements support for the Winbond W83627EHF, W83627EHG, and
super I/O chips. We will refer to them collectively as Winbond chips. W83627DHG super I/O 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, alarms with beep warnings (control speed sensors, ten analog voltage sensors (only nine for the 627DHG), alarms
unimplemented), and some automatic fan regulation strategies (plus manual with beep warnings (control unimplemented), and some automatic fan regulation
fan control mode). strategies (plus manual fan control mode).
Temperatures are measured in degrees Celsius and measurement resolution is 1 Temperatures are measured in degrees Celsius and measurement resolution is 1
degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when
@ -55,6 +58,9 @@ prog -> pwm4 (the programmable setting is not supported by the driver)
/sys files /sys files
---------- ----------
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"
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)
@ -83,3 +89,37 @@ pwm[1-4]_stop_time - how many milliseconds [ms] must elapse to switch
Note: last two functions are influenced by other control bits, not yet exported Note: last two functions are influenced by other control bits, not yet exported
by the driver, so a change might not have any effect. by the driver, so a change might not have any effect.
Implementation Details
----------------------
Future driver development should bear in mind that the following registers have
different functions on the 627EHF and the 627DHG. Some registers also have
different power-on default values, but BIOS should already be loading
appropriate defaults. Note that bank selection must be performed as is currently
done in the driver for all register addresses.
0x49: only on DHG, selects temperature source for AUX fan, CPU fan0
0x4a: not completely documented for the EHF and the DHG documentation assigns
different behavior to bits 7 and 6, including extending the temperature
input selection to SmartFan I, not just SmartFan III. Testing on the EHF
will reveal whether they are compatible or not.
0x58: Chip ID: 0xa1=EHF 0xc1=DHG
0x5e: only on DHG, has bits to enable "current mode" temperature detection and
critical temperature protection
0x45b: only on EHF, bit 3, vin4 alarm (EHF supports 10 inputs, only 9 on DHG)
0x552: only on EHF, vin4
0x558: only on EHF, vin4 high limit
0x559: only on EHF, vin4 low limit
0x6b: only on DHG, SYS fan critical temperature
0x6c: only on DHG, CPU fan0 critical temperature
0x6d: only on DHG, AUX fan critical temperature
0x6e: only on DHG, CPU fan1 critical temperature
0x50-0x55 and 0x650-0x657 are marked "Test Register" for the EHF, but "Reserved
Register" for the DHG
The DHG also supports PECI, where the DHG queries Intel CPU temperatures, and
the ICH8 southbridge gets that data via PECI from the DHG, so that the
southbridge drives the fans. And the DHG supports SST, a one-wire serial bus.

View File

@ -275,6 +275,12 @@ M: khali@linux-fr.org
L: lm-sensors@lm-sensors.org L: lm-sensors@lm-sensors.org
S: Maintained S: Maintained
ADM1029 HARDWARE MONITOR DRIVER
P: Corentin Labbe
M: corentin.labbe@geomatys.fr
L: lm-sensors@lm-sensors.org
S: Maintained
ADT746X FAN DRIVER ADT746X FAN DRIVER
P: Colin Leroy P: Colin Leroy
M: colin@colino.net M: colin@colino.net

View File

@ -73,6 +73,17 @@ config SENSORS_ADM1026
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 adm1026. will be called adm1026.
config SENSORS_ADM1029
tristate "Analog Devices ADM1029"
depends on HWMON && I2C && EXPERIMENTAL
help
If you say yes here you get support for Analog Devices ADM1029
sensor chip.
Very rare chip, please let us know you use it.
This driver can also be built as a module. If so, the module
will be called adm1029.
config SENSORS_ADM1031 config SENSORS_ADM1031
tristate "Analog Devices ADM1031 and compatibles" tristate "Analog Devices ADM1031 and compatibles"
depends on HWMON && I2C && EXPERIMENTAL depends on HWMON && I2C && EXPERIMENTAL

View File

@ -17,6 +17,7 @@ obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o
obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o
obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o
obj-$(CONFIG_SENSORS_ADM1029) += adm1029.o
obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o
obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o
obj-$(CONFIG_SENSORS_AMS) += ams/ obj-$(CONFIG_SENSORS_AMS) += ams/

View File

@ -1267,30 +1267,42 @@ static int __devinit abituguru_probe(struct platform_device *pdev)
printk(KERN_INFO ABIT_UGURU_NAME ": found Abit uGuru\n"); printk(KERN_INFO ABIT_UGURU_NAME ": found Abit uGuru\n");
/* Register sysfs hooks */ /* Register sysfs hooks */
data->class_dev = hwmon_device_register(&pdev->dev);
if (IS_ERR(data->class_dev)) {
res = PTR_ERR(data->class_dev);
goto abituguru_probe_error;
}
for (i = 0; i < sysfs_attr_i; i++) for (i = 0; i < sysfs_attr_i; i++)
device_create_file(&pdev->dev, &data->sysfs_attr[i].dev_attr); if (device_create_file(&pdev->dev,
&data->sysfs_attr[i].dev_attr))
goto abituguru_probe_error;
for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++) for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++)
device_create_file(&pdev->dev, if (device_create_file(&pdev->dev,
&abituguru_sysfs_attr[i].dev_attr); &abituguru_sysfs_attr[i].dev_attr))
goto abituguru_probe_error;
return 0; data->class_dev = hwmon_device_register(&pdev->dev);
if (!IS_ERR(data->class_dev))
return 0; /* success */
res = PTR_ERR(data->class_dev);
abituguru_probe_error: abituguru_probe_error:
for (i = 0; data->sysfs_attr[i].dev_attr.attr.name; i++)
device_remove_file(&pdev->dev, &data->sysfs_attr[i].dev_attr);
for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++)
device_remove_file(&pdev->dev,
&abituguru_sysfs_attr[i].dev_attr);
kfree(data); kfree(data);
return res; return res;
} }
static int __devexit abituguru_remove(struct platform_device *pdev) static int __devexit abituguru_remove(struct platform_device *pdev)
{ {
int i;
struct abituguru_data *data = platform_get_drvdata(pdev); struct abituguru_data *data = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
hwmon_device_unregister(data->class_dev); hwmon_device_unregister(data->class_dev);
for (i = 0; data->sysfs_attr[i].dev_attr.attr.name; i++)
device_remove_file(&pdev->dev, &data->sysfs_attr[i].dev_attr);
for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++)
device_remove_file(&pdev->dev,
&abituguru_sysfs_attr[i].dev_attr);
kfree(data); kfree(data);
return 0; return 0;

View File

@ -261,7 +261,6 @@ struct pwm_data {
struct adm1026_data { struct adm1026_data {
struct i2c_client client; struct i2c_client client;
struct class_device *class_dev; struct class_device *class_dev;
struct mutex lock;
enum chips type; enum chips type;
struct mutex update_lock; struct mutex update_lock;

508
drivers/hwmon/adm1029.c Normal file
View File

@ -0,0 +1,508 @@
/*
* adm1029.c - Part of lm_sensors, Linux kernel modules for hardware monitoring
*
* Copyright (C) 2006 Corentin LABBE <corentin.labbe@geomatys.fr>
*
* Based on LM83 Driver by Jean Delvare <khali@linux-fr.org>
*
* Give only processor, motherboard temperatures and fan tachs
* Very rare chip please let me know if you use it
*
* http://www.analog.com/UploadedFiles/Data_Sheets/ADM1029.pdf
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 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-sysfs.h>
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
/*
* Addresses to scan
*/
static unsigned short normal_i2c[] = {
0x28, 0x29, 0x2a,
0x2b, 0x2c, 0x2d,
0x2e, 0x2f, I2C_CLIENT_END
};
/*
* Insmod parameters
*/
I2C_CLIENT_INSMOD_1(adm1029);
/*
* The ADM1029 registers
* Manufacturer ID is 0x41 for Analog Devices
*/
#define ADM1029_REG_MAN_ID 0x0D
#define ADM1029_REG_CHIP_ID 0x0E
#define ADM1029_REG_CONFIG 0x01
#define ADM1029_REG_NB_FAN_SUPPORT 0x02
#define ADM1029_REG_TEMP_DEVICES_INSTALLED 0x06
#define ADM1029_REG_LOCAL_TEMP 0xA0
#define ADM1029_REG_REMOTE1_TEMP 0xA1
#define ADM1029_REG_REMOTE2_TEMP 0xA2
#define ADM1029_REG_LOCAL_TEMP_HIGH 0x90
#define ADM1029_REG_REMOTE1_TEMP_HIGH 0x91
#define ADM1029_REG_REMOTE2_TEMP_HIGH 0x92
#define ADM1029_REG_LOCAL_TEMP_LOW 0x98
#define ADM1029_REG_REMOTE1_TEMP_LOW 0x99
#define ADM1029_REG_REMOTE2_TEMP_LOW 0x9A
#define ADM1029_REG_FAN1 0x70
#define ADM1029_REG_FAN2 0x71
#define ADM1029_REG_FAN1_MIN 0x78
#define ADM1029_REG_FAN2_MIN 0x79
#define ADM1029_REG_FAN1_CONFIG 0x68
#define ADM1029_REG_FAN2_CONFIG 0x69
#define TEMP_FROM_REG(val) ((val) * 1000)
#define DIV_FROM_REG(val) ( 1 << (((val) >> 6) - 1))
/* Registers to be checked by adm1029_update_device() */
static const u8 ADM1029_REG_TEMP[] = {
ADM1029_REG_LOCAL_TEMP,
ADM1029_REG_REMOTE1_TEMP,
ADM1029_REG_REMOTE2_TEMP,
ADM1029_REG_LOCAL_TEMP_HIGH,
ADM1029_REG_REMOTE1_TEMP_HIGH,
ADM1029_REG_REMOTE2_TEMP_HIGH,
ADM1029_REG_LOCAL_TEMP_LOW,
ADM1029_REG_REMOTE1_TEMP_LOW,
ADM1029_REG_REMOTE2_TEMP_LOW,
};
static const u8 ADM1029_REG_FAN[] = {
ADM1029_REG_FAN1,
ADM1029_REG_FAN2,
ADM1029_REG_FAN1_MIN,
ADM1029_REG_FAN2_MIN,
};
static const u8 ADM1029_REG_FAN_DIV[] = {
ADM1029_REG_FAN1_CONFIG,
ADM1029_REG_FAN2_CONFIG,
};
/*
* Functions declaration
*/
static int adm1029_attach_adapter(struct i2c_adapter *adapter);
static int adm1029_detect(struct i2c_adapter *adapter, int address, int kind);
static int adm1029_detach_client(struct i2c_client *client);
static struct adm1029_data *adm1029_update_device(struct device *dev);
static int adm1029_init_client(struct i2c_client *client);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver adm1029_driver = {
.driver = {
.name = "adm1029",
},
.attach_adapter = adm1029_attach_adapter,
.detach_client = adm1029_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct adm1029_data {
struct i2c_client client;
struct class_device *class_dev;
struct mutex update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
/* registers values, signed for temperature, unsigned for other stuff */
s8 temp[ARRAY_SIZE(ADM1029_REG_TEMP)];
u8 fan[ARRAY_SIZE(ADM1029_REG_FAN)];
u8 fan_div[ARRAY_SIZE(ADM1029_REG_FAN_DIV)];
};
/*
* Sysfs stuff
*/
static ssize_t
show_temp(struct device *dev, struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adm1029_data *data = adm1029_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index]));
}
static ssize_t
show_fan(struct device *dev, struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adm1029_data *data = adm1029_update_device(dev);
u16 val;
if (data->fan[attr->index] == 0 || data->fan_div[attr->index] == 0
|| data->fan[attr->index] == 255) {
return sprintf(buf, "0\n");
}
val = 1880 * 120 / DIV_FROM_REG(data->fan_div[attr->index])
/ data->fan[attr->index];
return sprintf(buf, "%d\n", val);
}
static ssize_t
show_fan_div(struct device *dev, struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct adm1029_data *data = adm1029_update_device(dev);
if (data->fan_div[attr->index] == 0)
return sprintf(buf, "0\n");
return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
}
static ssize_t set_fan_div(struct device *dev,
struct device_attribute *devattr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1029_data *data = i2c_get_clientdata(client);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
long val = simple_strtol(buf, NULL, 10);
u8 reg;
mutex_lock(&data->update_lock);
/*Read actual config */
reg = i2c_smbus_read_byte_data(client,
ADM1029_REG_FAN_DIV[attr->index]);
switch (val) {
case 1:
val = 1;
break;
case 2:
val = 2;
break;
case 4:
val = 3;
break;
default:
mutex_unlock(&data->update_lock);
dev_err(&client->dev, "fan_div value %ld not "
"supported. Choose one of 1, 2 or 4!\n", val);
return -EINVAL;
}
/* Update the value */
reg = (reg & 0x3F) | (val << 6);
/* Write value */
i2c_smbus_write_byte_data(client,
ADM1029_REG_FAN_DIV[attr->index], reg);
mutex_unlock(&data->update_lock);
return count;
}
/*
Access rights on sysfs, S_IRUGO stand for Is Readable by User, Group and Others
S_IWUSR stand for Is Writable by User
*/
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL, 3);
static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp, NULL, 4);
static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp, NULL, 5);
static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp, NULL, 6);
static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp, NULL, 7);
static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp, NULL, 8);
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO, show_fan, NULL, 2);
static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO, show_fan, NULL, 3);
static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR,
show_fan_div, set_fan_div, 0);
static SENSOR_DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR,
show_fan_div, set_fan_div, 1);
static struct attribute *adm1029_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_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp2_min.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp3_input.dev_attr.attr,
&sensor_dev_attr_temp3_min.dev_attr.attr,
&sensor_dev_attr_temp3_max.dev_attr.attr,
&sensor_dev_attr_fan1_input.dev_attr.attr,
&sensor_dev_attr_fan2_input.dev_attr.attr,
&sensor_dev_attr_fan1_min.dev_attr.attr,
&sensor_dev_attr_fan2_min.dev_attr.attr,
&sensor_dev_attr_fan1_div.dev_attr.attr,
&sensor_dev_attr_fan2_div.dev_attr.attr,
NULL
};
static const struct attribute_group adm1029_group = {
.attrs = adm1029_attributes,
};
/*
* Real code
*/
static int adm1029_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_probe(adapter, &addr_data, adm1029_detect);
}
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
*/
static int adm1029_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *client;
struct adm1029_data *data;
int err = 0;
const char *name = "";
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kzalloc(sizeof(struct adm1029_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
client = &data->client;
i2c_set_clientdata(client, data);
client->addr = address;
client->adapter = adapter;
client->driver = &adm1029_driver;
/* Now we do the detection and identification. A negative kind
* means that the driver was loaded with no force parameter
* (default), so we must both detect and identify the chip
* (actually there is only one possible kind of chip for now, adm1029).
* A zero kind means that the driver was loaded with the force
* parameter, the detection step shall be skipped. A positive kind
* means that the driver was loaded with the force parameter and a
* given kind of chip is requested, so both the detection and the
* identification steps are skipped. */
/* Default to an adm1029 if forced */
if (kind == 0)
kind = adm1029;
/* ADM1029 doesn't have CHIP ID, check just MAN ID
* For better detection we check also ADM1029_TEMP_DEVICES_INSTALLED,
* ADM1029_REG_NB_FAN_SUPPORT and compare it with possible values
* documented
*/
if (kind <= 0) { /* identification */
u8 man_id, chip_id, temp_devices_installed, nb_fan_support;
man_id = i2c_smbus_read_byte_data(client, ADM1029_REG_MAN_ID);
chip_id = i2c_smbus_read_byte_data(client, ADM1029_REG_CHIP_ID);
temp_devices_installed = i2c_smbus_read_byte_data(client,
ADM1029_REG_TEMP_DEVICES_INSTALLED);
nb_fan_support = i2c_smbus_read_byte_data(client,
ADM1029_REG_NB_FAN_SUPPORT);
/* 0x41 is Analog Devices */
if (man_id == 0x41 && (temp_devices_installed & 0xf9) == 0x01
&& nb_fan_support == 0x03) {
if ((chip_id & 0xF0) == 0x00) {
kind = adm1029;
} else {
/* There are no "official" CHIP ID, so actually
* we use Major/Minor revision for that */
printk(KERN_INFO
"adm1029: Unknown major revision %x, "
"please let us know\n", chip_id);
}
}
if (kind <= 0) { /* identification failed */
pr_debug("adm1029: Unsupported chip (man_id=0x%02X, "
"chip_id=0x%02X)\n", man_id, chip_id);
goto exit_free;
}
}
if (kind == adm1029) {
name = "adm1029";
}
/* We can fill in the remaining client fields */
strlcpy(client->name, name, I2C_NAME_SIZE);
mutex_init(&data->update_lock);
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(client)))
goto exit_free;
/*
* Initialize the ADM1029 chip
* Check config register
*/
if (adm1029_init_client(client) == 0)
goto exit_detach;
/* Register sysfs hooks */
if ((err = sysfs_create_group(&client->dev.kobj, &adm1029_group)))
goto exit_detach;
data->class_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
goto exit_remove_files;
}
return 0;
exit_remove_files:
sysfs_remove_group(&client->dev.kobj, &adm1029_group);
exit_detach:
i2c_detach_client(client);
exit_free:
kfree(data);
exit:
return err;
}
static int adm1029_init_client(struct i2c_client *client)
{
u8 config;
config = i2c_smbus_read_byte_data(client, ADM1029_REG_CONFIG);
if ((config & 0x10) == 0) {
i2c_smbus_write_byte_data(client, ADM1029_REG_CONFIG,
config | 0x10);
}
/* recheck config */
config = i2c_smbus_read_byte_data(client, ADM1029_REG_CONFIG);
if ((config & 0x10) == 0) {
dev_err(&client->dev, "Initialization failed!\n");
return 0;
}
return 1;
}
static int adm1029_detach_client(struct i2c_client *client)
{
struct adm1029_data *data = i2c_get_clientdata(client);
int err;
hwmon_device_unregister(data->class_dev);
sysfs_remove_group(&client->dev.kobj, &adm1029_group);
if ((err = i2c_detach_client(client)))
return err;
kfree(data);
return 0;
}
/*
function that update the status of the chips (temperature for exemple)
*/
static struct adm1029_data *adm1029_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1029_data *data = i2c_get_clientdata(client);
mutex_lock(&data->update_lock);
/*
* Use the "cache" Luke, don't recheck values
* if there are already checked not a long time later
*/
if (time_after(jiffies, data->last_updated + HZ * 2)
|| !data->valid) {
int nr;
dev_dbg(&client->dev, "Updating adm1029 data\n");
for (nr = 0; nr < ARRAY_SIZE(ADM1029_REG_TEMP); nr++) {
data->temp[nr] =
i2c_smbus_read_byte_data(client,
ADM1029_REG_TEMP[nr]);
}
for (nr = 0; nr < ARRAY_SIZE(ADM1029_REG_FAN); nr++) {
data->fan[nr] =
i2c_smbus_read_byte_data(client,
ADM1029_REG_FAN[nr]);
}
for (nr = 0; nr < ARRAY_SIZE(ADM1029_REG_FAN_DIV); nr++) {
data->fan_div[nr] =
i2c_smbus_read_byte_data(client,
ADM1029_REG_FAN_DIV[nr]);
}
data->last_updated = jiffies;
data->valid = 1;
}
mutex_unlock(&data->update_lock);
return data;
}
/*
Common module stuff
*/
static int __init sensors_adm1029_init(void)
{
return i2c_add_driver(&adm1029_driver);
}
static void __exit sensors_adm1029_exit(void)
{
i2c_del_driver(&adm1029_driver);
}
MODULE_AUTHOR("Corentin LABBE <corentin.labbe@geomatys.fr>");
MODULE_DESCRIPTION("adm1029 driver");
MODULE_LICENSE("GPL v2");
module_init(sensors_adm1029_init);
module_exit(sensors_adm1029_exit);

View File

@ -146,7 +146,6 @@ superio_exit(int base)
struct f71805f_data { struct f71805f_data {
unsigned short addr; unsigned short addr;
const char *name; const char *name;
struct mutex lock;
struct class_device *class_dev; struct class_device *class_dev;
struct mutex update_lock; struct mutex update_lock;
@ -271,50 +270,42 @@ static inline u8 temp_to_reg(long val)
* Device I/O access * Device I/O access
*/ */
/* Must be called with data->update_lock held, except during initialization */
static u8 f71805f_read8(struct f71805f_data *data, u8 reg) static u8 f71805f_read8(struct f71805f_data *data, u8 reg)
{ {
u8 val;
mutex_lock(&data->lock);
outb(reg, data->addr + ADDR_REG_OFFSET); outb(reg, data->addr + ADDR_REG_OFFSET);
val = inb(data->addr + DATA_REG_OFFSET); return inb(data->addr + DATA_REG_OFFSET);
mutex_unlock(&data->lock);
return val;
} }
/* Must be called with data->update_lock held, except during initialization */
static void f71805f_write8(struct f71805f_data *data, u8 reg, u8 val) static void f71805f_write8(struct f71805f_data *data, u8 reg, u8 val)
{ {
mutex_lock(&data->lock);
outb(reg, data->addr + ADDR_REG_OFFSET); outb(reg, data->addr + ADDR_REG_OFFSET);
outb(val, data->addr + DATA_REG_OFFSET); outb(val, data->addr + DATA_REG_OFFSET);
mutex_unlock(&data->lock);
} }
/* It is important to read the MSB first, because doing so latches the /* It is important to read the MSB first, because doing so latches the
value of the LSB, so we are sure both bytes belong to the same value. */ value of the LSB, so we are sure both bytes belong to the same value.
Must be called with data->update_lock held, except during initialization */
static u16 f71805f_read16(struct f71805f_data *data, u8 reg) static u16 f71805f_read16(struct f71805f_data *data, u8 reg)
{ {
u16 val; u16 val;
mutex_lock(&data->lock);
outb(reg, data->addr + ADDR_REG_OFFSET); outb(reg, data->addr + ADDR_REG_OFFSET);
val = inb(data->addr + DATA_REG_OFFSET) << 8; val = inb(data->addr + DATA_REG_OFFSET) << 8;
outb(++reg, data->addr + ADDR_REG_OFFSET); outb(++reg, data->addr + ADDR_REG_OFFSET);
val |= inb(data->addr + DATA_REG_OFFSET); val |= inb(data->addr + DATA_REG_OFFSET);
mutex_unlock(&data->lock);
return val; return val;
} }
/* Must be called with data->update_lock held, except during initialization */
static void f71805f_write16(struct f71805f_data *data, u8 reg, u16 val) static void f71805f_write16(struct f71805f_data *data, u8 reg, u16 val)
{ {
mutex_lock(&data->lock);
outb(reg, data->addr + ADDR_REG_OFFSET); outb(reg, data->addr + ADDR_REG_OFFSET);
outb(val >> 8, data->addr + DATA_REG_OFFSET); outb(val >> 8, data->addr + DATA_REG_OFFSET);
outb(++reg, data->addr + ADDR_REG_OFFSET); outb(++reg, data->addr + ADDR_REG_OFFSET);
outb(val & 0xff, data->addr + DATA_REG_OFFSET); outb(val & 0xff, data->addr + DATA_REG_OFFSET);
mutex_unlock(&data->lock);
} }
static struct f71805f_data *f71805f_update_device(struct device *dev) static struct f71805f_data *f71805f_update_device(struct device *dev)
@ -1150,7 +1141,6 @@ static int __devinit f71805f_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_IO, 0); res = platform_get_resource(pdev, IORESOURCE_IO, 0);
data->addr = res->start; data->addr = res->start;
mutex_init(&data->lock);
data->name = names[sio_data->kind]; data->name = names[sio_data->kind];
mutex_init(&data->update_lock); mutex_init(&data->update_lock);
@ -1300,14 +1290,11 @@ static int __init f71805f_device_add(unsigned short address,
if (err) { if (err) {
printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
err); err);
goto exit_kfree_data; goto exit_device_put;
} }
return 0; return 0;
exit_kfree_data:
kfree(pdev->dev.platform_data);
pdev->dev.platform_data = NULL;
exit_device_put: exit_device_put:
platform_device_put(pdev); platform_device_put(pdev);
exit: exit:
@ -1400,10 +1387,7 @@ exit:
static void __exit f71805f_exit(void) static void __exit f71805f_exit(void)
{ {
kfree(pdev->dev.platform_data);
pdev->dev.platform_data = NULL;
platform_device_unregister(pdev); platform_device_unregister(pdev);
platform_driver_unregister(&f71805f_driver); platform_driver_unregister(&f71805f_driver);
} }

View File

@ -101,7 +101,7 @@ static void __exit hwmon_exit(void)
class_destroy(hwmon_class); class_destroy(hwmon_class);
} }
module_init(hwmon_init); subsys_initcall(hwmon_init);
module_exit(hwmon_exit); module_exit(hwmon_exit);
EXPORT_SYMBOL_GPL(hwmon_device_register); EXPORT_SYMBOL_GPL(hwmon_device_register);

View File

@ -202,15 +202,23 @@ static int DIV_TO_REG(int val)
} }
#define DIV_FROM_REG(val) (1 << (val)) #define DIV_FROM_REG(val) (1 << (val))
static const unsigned int pwm_freq[8] = {
48000000 / 128,
24000000 / 128,
12000000 / 128,
8000000 / 128,
6000000 / 128,
3000000 / 128,
1500000 / 128,
750000 / 128,
};
/* For each registered IT87, we need to keep some data in memory. That
data is pointed to by it87_list[NR]->data. The structure itself is /* For each registered chip, we need to keep some data in memory.
dynamically allocated, at the same time when a new it87 client is The structure is dynamically allocated. */
allocated. */
struct it87_data { struct it87_data {
struct i2c_client client; struct i2c_client client;
struct class_device *class_dev; struct class_device *class_dev;
struct mutex lock;
enum chips type; enum chips type;
struct mutex update_lock; struct mutex update_lock;
@ -232,6 +240,7 @@ struct it87_data {
u8 vrm; u8 vrm;
u32 alarms; /* Register encoding, combined */ u32 alarms; /* Register encoding, combined */
u8 fan_main_ctrl; /* Register value */ u8 fan_main_ctrl; /* Register value */
u8 fan_ctl; /* Register value */
u8 manual_pwm_ctl[3]; /* manual PWM value set by user */ u8 manual_pwm_ctl[3]; /* manual PWM value set by user */
}; };
@ -519,6 +528,14 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr,
struct it87_data *data = it87_update_device(dev); struct it87_data *data = it87_update_device(dev);
return sprintf(buf,"%d\n", data->manual_pwm_ctl[nr]); return sprintf(buf,"%d\n", data->manual_pwm_ctl[nr]);
} }
static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct it87_data *data = it87_update_device(dev);
int index = (data->fan_ctl >> 4) & 0x07;
return sprintf(buf, "%u\n", pwm_freq[index]);
}
static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
@ -528,9 +545,10 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr,
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = to_i2c_client(dev);
struct it87_data *data = i2c_get_clientdata(client); struct it87_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10); int val = simple_strtol(buf, NULL, 10);
u8 reg = it87_read_value(client, IT87_REG_FAN_DIV); u8 reg;
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
reg = it87_read_value(client, IT87_REG_FAN_DIV);
switch (nr) { switch (nr) {
case 0: data->fan_div[nr] = reg & 0x07; break; case 0: data->fan_div[nr] = reg & 0x07; break;
case 1: data->fan_div[nr] = (reg >> 3) & 0x07; break; case 1: data->fan_div[nr] = (reg >> 3) & 0x07; break;
@ -639,6 +657,28 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
return count; return count;
} }
static ssize_t set_pwm_freq(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct it87_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
int i;
/* Search for the nearest available frequency */
for (i = 0; i < 7; i++) {
if (val > (pwm_freq[i] + pwm_freq[i+1]) / 2)
break;
}
mutex_lock(&data->update_lock);
data->fan_ctl = it87_read_value(client, IT87_REG_FAN_CTL) & 0x8f;
data->fan_ctl |= i << 4;
it87_write_value(client, IT87_REG_FAN_CTL, data->fan_ctl);
mutex_unlock(&data->update_lock);
return count;
}
#define show_fan_offset(offset) \ #define show_fan_offset(offset) \
static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \
@ -656,7 +696,10 @@ show_fan_offset(3);
static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \ static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
show_pwm_enable, set_pwm_enable, offset - 1); \ show_pwm_enable, set_pwm_enable, offset - 1); \
static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
show_pwm, set_pwm, offset - 1); show_pwm, set_pwm, offset - 1); \
static DEVICE_ATTR(pwm##offset##_freq, \
(offset == 1 ? S_IRUGO | S_IWUSR : S_IRUGO), \
show_pwm_freq, (offset == 1 ? set_pwm_freq : NULL));
show_pwm_offset(1); show_pwm_offset(1);
show_pwm_offset(2); show_pwm_offset(2);
@ -904,7 +947,6 @@ static int it87_detect(struct i2c_adapter *adapter)
} }
new_client = &data->client; new_client = &data->client;
mutex_init(&data->lock);
i2c_set_clientdata(new_client, data); i2c_set_clientdata(new_client, data);
new_client->addr = isa_address; new_client->addr = isa_address;
new_client->adapter = adapter; new_client->adapter = adapter;
@ -1021,7 +1063,13 @@ static int it87_detect(struct i2c_adapter *adapter)
|| (err = device_create_file(&new_client->dev, || (err = device_create_file(&new_client->dev,
&sensor_dev_attr_pwm2.dev_attr)) &sensor_dev_attr_pwm2.dev_attr))
|| (err = device_create_file(&new_client->dev, || (err = device_create_file(&new_client->dev,
&sensor_dev_attr_pwm3.dev_attr))) &sensor_dev_attr_pwm3.dev_attr))
|| (err = device_create_file(&new_client->dev,
&dev_attr_pwm1_freq))
|| (err = device_create_file(&new_client->dev,
&dev_attr_pwm2_freq))
|| (err = device_create_file(&new_client->dev,
&dev_attr_pwm3_freq)))
goto ERROR4; goto ERROR4;
} }
@ -1076,33 +1124,22 @@ static int it87_detach_client(struct i2c_client *client)
return 0; return 0;
} }
/* ISA access must be locked explicitly! /* Must be called with data->update_lock held, except during initialization.
We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks,
would slow down the IT87 access and should not be necessary. */ would slow down the IT87 access and should not be necessary. */
static int it87_read_value(struct i2c_client *client, u8 reg) static int it87_read_value(struct i2c_client *client, u8 reg)
{ {
struct it87_data *data = i2c_get_clientdata(client);
int res;
mutex_lock(&data->lock);
outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET);
res = inb_p(client->addr + IT87_DATA_REG_OFFSET); return inb_p(client->addr + IT87_DATA_REG_OFFSET);
mutex_unlock(&data->lock);
return res;
} }
/* ISA access must be locked explicitly! /* Must be called with data->update_lock held, except during initialization.
We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks,
would slow down the IT87 access and should not be necessary. */ would slow down the IT87 access and should not be necessary. */
static void it87_write_value(struct i2c_client *client, u8 reg, u8 value) static void it87_write_value(struct i2c_client *client, u8 reg, u8 value)
{ {
struct it87_data *data = i2c_get_clientdata(client);
mutex_lock(&data->lock);
outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET);
outb_p(value, client->addr + IT87_DATA_REG_OFFSET); outb_p(value, client->addr + IT87_DATA_REG_OFFSET);
mutex_unlock(&data->lock);
} }
/* Return 1 if and only if the PWM interface is safe to use */ /* Return 1 if and only if the PWM interface is safe to use */
@ -1316,6 +1353,7 @@ static struct it87_data *it87_update_device(struct device *dev)
(it87_read_value(client, IT87_REG_ALARM2) << 8) | (it87_read_value(client, IT87_REG_ALARM2) << 8) |
(it87_read_value(client, IT87_REG_ALARM3) << 16); (it87_read_value(client, IT87_REG_ALARM3) << 16);
data->fan_main_ctrl = it87_read_value(client, IT87_REG_FAN_MAIN_CTRL); data->fan_main_ctrl = it87_read_value(client, IT87_REG_FAN_MAIN_CTRL);
data->fan_ctl = it87_read_value(client, IT87_REG_FAN_CTL);
data->sensor = it87_read_value(client, IT87_REG_TEMP_ENABLE); data->sensor = it87_read_value(client, IT87_REG_TEMP_ENABLE);
/* The 8705 does not have VID capability */ /* The 8705 does not have VID capability */

View File

@ -126,7 +126,7 @@ out_dev_reg_failed:
return status; return status;
} }
static int __exit lm70_remove(struct spi_device *spi) static int __devexit lm70_remove(struct spi_device *spi)
{ {
struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev); struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev);

View File

@ -125,10 +125,8 @@ static inline int TEMP_FROM_REG(s8 val)
bad. Quite a lot of bookkeeping is done. A real driver can often cut bad. Quite a lot of bookkeeping is done. A real driver can often cut
some corners. */ some corners. */
/* For each registered LM78, we need to keep some data in memory. That /* For each registered chip, we need to keep some data in memory.
data is pointed to by lm78_list[NR]->data. The structure itself is The structure is dynamically allocated. */
dynamically allocated, at the same time when a new lm78 client is
allocated. */
struct lm78_data { struct lm78_data {
struct i2c_client client; struct i2c_client client;
struct class_device *class_dev; struct class_device *class_dev;

View File

@ -298,11 +298,6 @@ static int ZONE_TO_REG( int zone )
#define LM85_DATA_INTERVAL (HZ + HZ / 2) #define LM85_DATA_INTERVAL (HZ + HZ / 2)
#define LM85_CONFIG_INTERVAL (1 * 60 * HZ) #define LM85_CONFIG_INTERVAL (1 * 60 * HZ)
/* For each registered LM85, we need to keep some data in memory. That
data is pointed to by lm85_list[NR]->data. The structure itself is
dynamically allocated, at the same time when a new lm85 client is
allocated. */
/* LM85 can automatically adjust fan speeds based on temperature /* LM85 can automatically adjust fan speeds based on temperature
* This structure encapsulates an entire Zone config. There are * This structure encapsulates an entire Zone config. There are
* three zones (one for each temperature input) on the lm85 * three zones (one for each temperature input) on the lm85
@ -329,10 +324,11 @@ struct lm85_autofan {
u8 min_off; /* Min PWM or OFF below "limit", flag */ u8 min_off; /* Min PWM or OFF below "limit", flag */
}; };
/* For each registered chip, we need to keep some data in memory.
The structure is dynamically allocated. */
struct lm85_data { struct lm85_data {
struct i2c_client client; struct i2c_client client;
struct class_device *class_dev; struct class_device *class_dev;
struct mutex lock;
enum chips type; enum chips type;
struct mutex update_lock; struct mutex update_lock;

View File

@ -162,10 +162,8 @@ static inline u8 DIV_TO_REG(int val)
} }
#define DIV_FROM_REG(val) (1 << (val)) #define DIV_FROM_REG(val) (1 << (val))
/* For the SIS5595, we need to keep some data in memory. That /* For each registered chip, we need to keep some data in memory.
data is pointed to by sis5595_list[NR]->data. The structure itself is The structure is dynamically allocated. */
dynamically allocated, at the time when the new sis5595 client is
allocated. */
struct sis5595_data { struct sis5595_data {
struct i2c_client client; struct i2c_client client;
struct class_device *class_dev; struct class_device *class_dev;

View File

@ -292,9 +292,8 @@ static inline long TEMP_FROM_REG10(u16 val)
#define DIV_FROM_REG(val) (1 << (val)) #define DIV_FROM_REG(val) (1 << (val))
#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) #define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
/* For the VIA686A, we need to keep some data in memory. /* For each registered chip, we need to keep some data in memory.
The structure is dynamically allocated, at the same time when a new The structure is dynamically allocated. */
via686a client is allocated. */
struct via686a_data { struct via686a_data {
struct i2c_client client; struct i2c_client client;
struct class_device *class_dev; struct class_device *class_dev;

View File

@ -178,9 +178,10 @@ struct vt1211_data {
* Super-I/O constants and functions * Super-I/O constants and functions
* --------------------------------------------------------------------- */ * --------------------------------------------------------------------- */
/* Configuration & data index port registers */ /* Configuration index port registers
#define SIO_REG_CIP 0x2e * The vt1211 can live at 2 different addresses so we need to probe both */
#define SIO_REG_DIP 0x2f #define SIO_REG_CIP1 0x2e
#define SIO_REG_CIP2 0x4e
/* Configuration registers */ /* Configuration registers */
#define SIO_VT1211_LDN 0x07 /* logical device number */ #define SIO_VT1211_LDN 0x07 /* logical device number */
@ -193,33 +194,33 @@ struct vt1211_data {
/* VT1211 logical device numbers */ /* VT1211 logical device numbers */
#define SIO_VT1211_LDN_HWMON 0x0b /* HW monitor */ #define SIO_VT1211_LDN_HWMON 0x0b /* HW monitor */
static inline void superio_outb(int reg, int val) static inline void superio_outb(int sio_cip, int reg, int val)
{ {
outb(reg, SIO_REG_CIP); outb(reg, sio_cip);
outb(val, SIO_REG_DIP); outb(val, sio_cip + 1);
} }
static inline int superio_inb(int reg) static inline int superio_inb(int sio_cip, int reg)
{ {
outb(reg, SIO_REG_CIP); outb(reg, sio_cip);
return inb(SIO_REG_DIP); return inb(sio_cip + 1);
} }
static inline void superio_select(int ldn) static inline void superio_select(int sio_cip, int ldn)
{ {
outb(SIO_VT1211_LDN, SIO_REG_CIP); outb(SIO_VT1211_LDN, sio_cip);
outb(ldn, SIO_REG_DIP); outb(ldn, sio_cip + 1);
} }
static inline void superio_enter(void) static inline void superio_enter(int sio_cip)
{ {
outb(0x87, SIO_REG_CIP); outb(0x87, sio_cip);
outb(0x87, SIO_REG_CIP); outb(0x87, sio_cip);
} }
static inline void superio_exit(void) static inline void superio_exit(int sio_cip)
{ {
outb(0xaa, SIO_REG_CIP); outb(0xaa, sio_cip);
} }
/* --------------------------------------------------------------------- /* ---------------------------------------------------------------------
@ -1263,26 +1264,26 @@ EXIT:
return err; return err;
} }
static int __init vt1211_find(unsigned short *address) static int __init vt1211_find(int sio_cip, unsigned short *address)
{ {
int err = -ENODEV; int err = -ENODEV;
superio_enter(); superio_enter(sio_cip);
if (superio_inb(SIO_VT1211_DEVID) != SIO_VT1211_ID) { if (superio_inb(sio_cip, SIO_VT1211_DEVID) != SIO_VT1211_ID) {
goto EXIT; goto EXIT;
} }
superio_select(SIO_VT1211_LDN_HWMON); superio_select(sio_cip, SIO_VT1211_LDN_HWMON);
if ((superio_inb(SIO_VT1211_ACTIVE) & 1) == 0) { if ((superio_inb(sio_cip, SIO_VT1211_ACTIVE) & 1) == 0) {
printk(KERN_WARNING DRVNAME ": HW monitor is disabled, " printk(KERN_WARNING DRVNAME ": HW monitor is disabled, "
"skipping\n"); "skipping\n");
goto EXIT; goto EXIT;
} }
*address = ((superio_inb(SIO_VT1211_BADDR) << 8) | *address = ((superio_inb(sio_cip, SIO_VT1211_BADDR) << 8) |
(superio_inb(SIO_VT1211_BADDR + 1))) & 0xff00; (superio_inb(sio_cip, SIO_VT1211_BADDR + 1))) & 0xff00;
if (*address == 0) { if (*address == 0) {
printk(KERN_WARNING DRVNAME ": Base address is not set, " printk(KERN_WARNING DRVNAME ": Base address is not set, "
"skipping\n"); "skipping\n");
@ -1291,10 +1292,11 @@ static int __init vt1211_find(unsigned short *address)
err = 0; err = 0;
printk(KERN_INFO DRVNAME ": Found VT1211 chip at 0x%04x, " printk(KERN_INFO DRVNAME ": Found VT1211 chip at 0x%04x, "
"revision %u\n", *address, superio_inb(SIO_VT1211_DEVREV)); "revision %u\n", *address,
superio_inb(sio_cip, SIO_VT1211_DEVREV));
EXIT: EXIT:
superio_exit(); superio_exit(sio_cip);
return err; return err;
} }
@ -1303,8 +1305,8 @@ static int __init vt1211_init(void)
int err; int err;
unsigned short address = 0; unsigned short address = 0;
err = vt1211_find(&address); if ((err = vt1211_find(SIO_REG_CIP1, &address)) &&
if (err) { (err = vt1211_find(SIO_REG_CIP2, &address))) {
goto EXIT; goto EXIT;
} }

View File

@ -32,8 +32,10 @@
Supports the following chips: Supports the following chips:
Chip #vin #fan #pwm #temp chip_id man_id Chip #vin #fan #pwm #temp chip IDs man ID
w83627ehf 10 5 4 3 0x88,0xa1 0x5ca3 w83627ehf 10 5 4 3 0x8850 0x88 0x5ca3
0x8860 0xa1
w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3
*/ */
#include <linux/module.h> #include <linux/module.h>
@ -55,8 +57,18 @@ static unsigned short address;
* Super-I/O constants and functions * Super-I/O constants and functions
*/ */
/*
* The three following globals are initialized in w83627ehf_find(), before
* the i2c-isa device is created. Otherwise, they could be stored in
* w83627ehf_data. This is ugly, but necessary, and when the driver is next
* updated to become a platform driver, the globals will disappear.
*/
static int REG; /* The register to read/write */ static int REG; /* The register to read/write */
static int VAL; /* The value to read/write */ static int VAL; /* The value to read/write */
/* The w83627ehf/ehg have 10 voltage inputs, but the w83627dhg has 9. This
* value is also used in w83627ehf_detect() to export a device name in sysfs
* (e.g. w83627ehf or w83627dhg) */
static int w83627ehf_num_in;
#define W83627EHF_LD_HWM 0x0b #define W83627EHF_LD_HWM 0x0b
@ -65,8 +77,10 @@ static int VAL; /* The value to read/write */
#define SIO_REG_ENABLE 0x30 /* Logical device enable */ #define SIO_REG_ENABLE 0x30 /* Logical device enable */
#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
#define SIO_W83627EHF_ID 0x8840 #define SIO_W83627EHF_ID 0x8850
#define SIO_ID_MASK 0xFFC0 #define SIO_W83627EHG_ID 0x8860
#define SIO_W83627DHG_ID 0xa020
#define SIO_ID_MASK 0xFFF0
static inline void static inline void
superio_outb(int reg, int val) superio_outb(int reg, int val)
@ -115,8 +129,12 @@ superio_exit(void)
#define W83627EHF_REG_BANK 0x4E #define W83627EHF_REG_BANK 0x4E
#define W83627EHF_REG_CONFIG 0x40 #define W83627EHF_REG_CONFIG 0x40
#define W83627EHF_REG_CHIP_ID 0x49
#define W83627EHF_REG_MAN_ID 0x4F /* Not currently used:
* REG_MAN_ID has the value 0x5ca3 for all supported chips.
* REG_CHIP_ID == 0x88/0xa1/0xc1 depending on chip model.
* REG_MAN_ID is at port 0x4f
* REG_CHIP_ID is at port 0x58 */
static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 }; static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 };
static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c }; static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c };
@ -429,7 +447,7 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
} }
/* Measured voltages and limits */ /* Measured voltages and limits */
for (i = 0; i < 10; i++) { for (i = 0; i < w83627ehf_num_in; i++) {
data->in[i] = w83627ehf_read_value(client, data->in[i] = w83627ehf_read_value(client,
W83627EHF_REG_IN(i)); W83627EHF_REG_IN(i));
data->in_min[i] = w83627ehf_read_value(client, data->in_min[i] = w83627ehf_read_value(client,
@ -1121,7 +1139,7 @@ static void w83627ehf_device_remove_files(struct device *dev)
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_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 < 10; i++) { for (i = 0; i < w83627ehf_num_in; i++) {
device_remove_file(dev, &sda_in_input[i].dev_attr); device_remove_file(dev, &sda_in_input[i].dev_attr);
device_remove_file(dev, &sda_in_alarm[i].dev_attr); device_remove_file(dev, &sda_in_alarm[i].dev_attr);
device_remove_file(dev, &sda_in_min[i].dev_attr); device_remove_file(dev, &sda_in_min[i].dev_attr);
@ -1196,7 +1214,11 @@ static int w83627ehf_detect(struct i2c_adapter *adapter)
client->flags = 0; client->flags = 0;
dev = &client->dev; dev = &client->dev;
strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE); if (w83627ehf_num_in == 9)
strlcpy(client->name, "w83627dhg", I2C_NAME_SIZE);
else /* just say ehf. 627EHG is 627EHF in lead-free packaging. */
strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
data->valid = 0; data->valid = 0;
mutex_init(&data->update_lock); mutex_init(&data->update_lock);
@ -1246,7 +1268,7 @@ static int w83627ehf_detect(struct i2c_adapter *adapter)
goto exit_remove; goto exit_remove;
} }
for (i = 0; i < 10; i++) for (i = 0; i < w83627ehf_num_in; i++)
if ((err = device_create_file(dev, &sda_in_input[i].dev_attr)) if ((err = device_create_file(dev, &sda_in_input[i].dev_attr))
|| (err = device_create_file(dev, || (err = device_create_file(dev,
&sda_in_alarm[i].dev_attr)) &sda_in_alarm[i].dev_attr))
@ -1340,7 +1362,17 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr)
val = (superio_inb(SIO_REG_DEVID) << 8) val = (superio_inb(SIO_REG_DEVID) << 8)
| superio_inb(SIO_REG_DEVID + 1); | superio_inb(SIO_REG_DEVID + 1);
if ((val & SIO_ID_MASK) != SIO_W83627EHF_ID) { switch (val & SIO_ID_MASK) {
case SIO_W83627DHG_ID:
w83627ehf_num_in = 9;
break;
case SIO_W83627EHF_ID:
case SIO_W83627EHG_ID:
w83627ehf_num_in = 10;
break;
default:
printk(KERN_WARNING "w83627ehf: unsupported chip ID: 0x%04x\n",
val);
superio_exit(); superio_exit();
return -ENODEV; return -ENODEV;
} }

View File

@ -286,9 +286,8 @@ static inline u8 DIV_TO_REG(long val)
return ((u8) i); return ((u8) i);
} }
/* For each registered chip, we need to keep some data in memory. That /* For each registered chip, we need to keep some data in memory.
data is pointed to by w83627hf_list[NR]->data. The structure itself is The structure is dynamically allocated. */
dynamically allocated, at the same time when a new client is allocated. */
struct w83627hf_data { struct w83627hf_data {
struct i2c_client client; struct i2c_client client;
struct class_device *class_dev; struct class_device *class_dev;

View File

@ -221,14 +221,8 @@ DIV_TO_REG(long val, enum chips type)
a bit - except if there could be more than one SMBus. Groan. No solution a bit - except if there could be more than one SMBus. Groan. No solution
for this yet. */ for this yet. */
/* This module may seem overly long and complicated. In fact, it is not so /* For each registered chip, we need to keep some data in memory.
bad. Quite a lot of bookkeeping is done. A real driver can often cut The structure is dynamically allocated. */
some corners. */
/* For each registered W83781D, we need to keep some data in memory. That
data is pointed to by w83781d_list[NR]->data. The structure itself is
dynamically allocated, at the same time when a new w83781d client is
allocated. */
struct w83781d_data { struct w83781d_data {
struct i2c_client client; struct i2c_client client;
struct class_device *class_dev; struct class_device *class_dev;