Commit 3c6fae67 authored by Linus Torvalds's avatar Linus Torvalds

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

* 'hwmon-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6:
  hwmon: (fschmd) Add support for the FSC Hades IC
  hwmon: (fschmd) Add support for the FSC Syleus IC
  i2c-i801: Instantiate FSC hardware montioring chips
  dmi: Let dmi_walk() users pass private data
  hwmon: Define a standard interface for chassis intrusion detection
  Move the pcf8591 driver to hwmon
  hwmon: (w83627ehf) Only expose in6 or temp3 on the W83667HG
  hwmon: (w83627ehf) Add support for W83667HG
  hwmon: (w83627ehf) Invert fan pin variables logic
  hwmon: (hdaps) Fix Thinkpad X41 axis inversion
  hwmon: (hdaps) Allow inversion of separate axis
  hwmon: (ds1621) Clean up documentation
  hwmon: (ds1621) Avoid unneeded register access
  hwmon: (ds1621) Clean up register access
  hwmon: (ds1621) Reorder code statements
parents c4e1aa67 de15f093
...@@ -49,12 +49,9 @@ of up to +/- 0.5 degrees even when compared against precise temperature ...@@ -49,12 +49,9 @@ of up to +/- 0.5 degrees even when compared against precise temperature
readings. Be sure to have a high vs. low temperature limit gap of al least readings. Be sure to have a high vs. low temperature limit gap of al least
1.0 degree Celsius to avoid Tout "bouncing", though! 1.0 degree Celsius to avoid Tout "bouncing", though!
As for alarms, you can read the alarm status of the DS1621 via the 'alarms' The alarm bits are set when the high or low limits are met or exceeded and
/sys file interface. The result consists mainly of bit 6 and 5 of the are reset by the module as soon as the respective temperature ranges are
configuration register of the chip; bit 6 (0x40 or 64) is the high alarm left.
bit and bit 5 (0x20 or 32) the low one. These bits are set when the high or
low limits are met or exceeded and are reset by the module as soon as the
respective temperature ranges are left.
The alarm registers are in no way suitable to find out about the actual The alarm registers are in no way suitable to find out about the actual
status of Tout. They will only tell you about its history, whether or not status of Tout. They will only tell you about its history, whether or not
...@@ -64,45 +61,3 @@ with neither of the alarms set. ...@@ -64,45 +61,3 @@ with neither of the alarms set.
Temperature conversion of the DS1621 takes up to 1000ms; internal access to Temperature conversion of the DS1621 takes up to 1000ms; internal access to
non-volatile registers may last for 10ms or below. non-volatile registers may last for 10ms or below.
High Accuracy Temperature Reading
---------------------------------
As said before, the temperature issued via the 9-bit i2c-bus data is
somewhat arbitrary. Internally, the temperature conversion is of a
different kind that is explained (not so...) well in the DS1621 data sheet.
To cut the long story short: Inside the DS1621 there are two oscillators,
both of them biassed by a temperature coefficient.
Higher resolution of the temperature reading can be achieved using the
internal projection, which means taking account of REG_COUNT and REG_SLOPE
(the driver manages them):
Taken from Dallas Semiconductors App Note 068: 'Increasing Temperature
Resolution on the DS1620' and App Note 105: 'High Resolution Temperature
Measurement with Dallas Direct-to-Digital Temperature Sensors'
- Read the 9-bit temperature and strip the LSB (Truncate the .5 degs)
- The resulting value is TEMP_READ.
- Then, read REG_COUNT.
- And then, REG_SLOPE.
TEMP = TEMP_READ - 0.25 + ((REG_SLOPE - REG_COUNT) / REG_SLOPE)
Note that this is what the DONE bit in the DS1621 configuration register is
good for: Internally, one temperature conversion takes up to 1000ms. Before
that conversion is complete you will not be able to read valid things out
of REG_COUNT and REG_SLOPE. The DONE bit, as you may have guessed by now,
tells you whether the conversion is complete ("done", in plain English) and
thus, whether the values you read are good or not.
The DS1621 has two modes of operation: "Continuous" conversion, which can
be understood as the default stand-alone mode where the chip gets the
temperature and controls external devices via its Tout pin or tells other
i2c's about it if they care. The other mode is called "1SHOT", that means
that it only figures out about the temperature when it is explicitly told
to do so; this can be seen as power saving mode.
Now if you want to read REG_COUNT and REG_SLOPE, you have to either stop
the continuous conversions until the contents of these registers are valid,
or, in 1SHOT mode, you have to have one conversion made.
...@@ -365,6 +365,7 @@ energy[1-*]_input Cumulative energy use ...@@ -365,6 +365,7 @@ energy[1-*]_input Cumulative energy use
Unit: microJoule Unit: microJoule
RO RO
********** **********
* Alarms * * Alarms *
********** **********
...@@ -453,6 +454,27 @@ beep_mask Bitmask for beep. ...@@ -453,6 +454,27 @@ beep_mask Bitmask for beep.
RW RW
***********************
* Intrusion detection *
***********************
intrusion[0-*]_alarm
Chassis intrusion detection
0: OK
1: intrusion detected
RW
Contrary to regular alarm flags which clear themselves
automatically when read, this one sticks until cleared by
the user. This is done by writing 0 to the file. Writing
other values is unsupported.
intrusion[0-*]_beep
Chassis intrusion beep
0: disable
1: enable
RW
sysfs attribute writes interpretation sysfs attribute writes interpretation
------------------------------------- -------------------------------------
......
...@@ -2,30 +2,40 @@ Kernel driver w83627ehf ...@@ -2,30 +2,40 @@ Kernel driver w83627ehf
======================= =======================
Supported chips: Supported chips:
* Winbond W83627EHF/EHG/DHG (ISA access ONLY) * Winbond W83627EHF/EHG (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: Datasheet:
http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf http://www.nuvoton.com.tw/NR/rdonlyres/A6A258F0-F0C9-4F97-81C0-C4D29E7E943E/0/W83627EHF.pdf
DHG datasheet confidential. * Winbond W83627DHG
Prefix: 'w83627dhg'
Addresses scanned: ISA address retrieved from Super I/O registers
Datasheet:
http://www.nuvoton.com.tw/NR/rdonlyres/7885623D-A487-4CF9-A47F-30C5F73D6FE6/0/W83627DHG.pdf
* Winbond W83667HG
Prefix: 'w83667hg'
Addresses scanned: ISA address retrieved from Super I/O registers
Datasheet: not available
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> David Hubbard <david.c.hubbard@gmail.com>
Gong Jun <JGong@nuvoton.com>
Description Description
----------- -----------
This driver implements support for the Winbond W83627EHF, W83627EHG, and This driver implements support for the Winbond W83627EHF, W83627EHG,
W83627DHG super I/O chips. We will refer to them collectively as Winbond chips. W83627DHG and W83667HG 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 (only nine for the 627DHG), one speed sensors, ten analog voltage sensors (only nine for the 627DHG), one
VID (6 pins for the 627EHF/EHG, 8 pins for the 627DHG), alarms with beep VID (6 pins for the 627EHF/EHG, 8 pins for the 627DHG and 667HG), alarms
warnings (control unimplemented), and some automatic fan regulation with beep warnings (control unimplemented), and some automatic fan
strategies (plus manual fan control mode). regulation 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
...@@ -54,7 +64,8 @@ follows: ...@@ -54,7 +64,8 @@ follows:
temp1 -> pwm1 temp1 -> pwm1
temp2 -> pwm2 temp2 -> pwm2
temp3 -> pwm3 temp3 -> pwm3
prog -> pwm4 (the programmable setting is not supported by the driver) prog -> pwm4 (not on 667HG; the programmable setting is not supported by
the driver)
/sys files /sys files
---------- ----------
......
...@@ -68,7 +68,8 @@ static char * __init dmi_string(const struct dmi_header *dm, u8 s) ...@@ -68,7 +68,8 @@ static char * __init dmi_string(const struct dmi_header *dm, u8 s)
* pointing to completely the wrong place for example * pointing to completely the wrong place for example
*/ */
static void dmi_table(u8 *buf, int len, int num, static void dmi_table(u8 *buf, int len, int num,
void (*decode)(const struct dmi_header *)) void (*decode)(const struct dmi_header *, void *),
void *private_data)
{ {
u8 *data = buf; u8 *data = buf;
int i = 0; int i = 0;
...@@ -89,7 +90,7 @@ static void dmi_table(u8 *buf, int len, int num, ...@@ -89,7 +90,7 @@ static void dmi_table(u8 *buf, int len, int num,
while ((data - buf < len - 1) && (data[0] || data[1])) while ((data - buf < len - 1) && (data[0] || data[1]))
data++; data++;
if (data - buf < len - 1) if (data - buf < len - 1)
decode(dm); decode(dm, private_data);
data += 2; data += 2;
i++; i++;
} }
...@@ -99,7 +100,8 @@ static u32 dmi_base; ...@@ -99,7 +100,8 @@ static u32 dmi_base;
static u16 dmi_len; static u16 dmi_len;
static u16 dmi_num; static u16 dmi_num;
static int __init dmi_walk_early(void (*decode)(const struct dmi_header *)) static int __init dmi_walk_early(void (*decode)(const struct dmi_header *,
void *))
{ {
u8 *buf; u8 *buf;
...@@ -107,7 +109,7 @@ static int __init dmi_walk_early(void (*decode)(const struct dmi_header *)) ...@@ -107,7 +109,7 @@ static int __init dmi_walk_early(void (*decode)(const struct dmi_header *))
if (buf == NULL) if (buf == NULL)
return -1; return -1;
dmi_table(buf, dmi_len, dmi_num, decode); dmi_table(buf, dmi_len, dmi_num, decode, NULL);
dmi_iounmap(buf, dmi_len); dmi_iounmap(buf, dmi_len);
return 0; return 0;
...@@ -295,7 +297,7 @@ static void __init dmi_save_extended_devices(const struct dmi_header *dm) ...@@ -295,7 +297,7 @@ static void __init dmi_save_extended_devices(const struct dmi_header *dm)
* and machine entries. For 2.5 we should pull the smbus controller info * and machine entries. For 2.5 we should pull the smbus controller info
* out of here. * out of here.
*/ */
static void __init dmi_decode(const struct dmi_header *dm) static void __init dmi_decode(const struct dmi_header *dm, void *dummy)
{ {
switch(dm->type) { switch(dm->type) {
case 0: /* BIOS Information */ case 0: /* BIOS Information */
...@@ -598,10 +600,12 @@ int dmi_get_year(int field) ...@@ -598,10 +600,12 @@ int dmi_get_year(int field)
/** /**
* dmi_walk - Walk the DMI table and get called back for every record * dmi_walk - Walk the DMI table and get called back for every record
* @decode: Callback function * @decode: Callback function
* @private_data: Private data to be passed to the callback function
* *
* Returns -1 when the DMI table can't be reached, 0 on success. * Returns -1 when the DMI table can't be reached, 0 on success.
*/ */
int dmi_walk(void (*decode)(const struct dmi_header *)) int dmi_walk(void (*decode)(const struct dmi_header *, void *),
void *private_data)
{ {
u8 *buf; u8 *buf;
...@@ -612,7 +616,7 @@ int dmi_walk(void (*decode)(const struct dmi_header *)) ...@@ -612,7 +616,7 @@ int dmi_walk(void (*decode)(const struct dmi_header *))
if (buf == NULL) if (buf == NULL)
return -1; return -1;
dmi_table(buf, dmi_len, dmi_num, decode); dmi_table(buf, dmi_len, dmi_num, decode, private_data);
iounmap(buf); iounmap(buf);
return 0; return 0;
......
...@@ -343,12 +343,13 @@ config SENSORS_FSCPOS ...@@ -343,12 +343,13 @@ config SENSORS_FSCPOS
will be called fscpos. will be called fscpos.
config SENSORS_FSCHMD config SENSORS_FSCHMD
tristate "FSC Poseidon, Scylla, Hermes, Heimdall and Heracles" tristate "Fujitsu Siemens Computers sensor chips"
depends on X86 && I2C depends on X86 && I2C
help help
If you say yes here you get support for various Fujitsu Siemens If you say yes here you get support for the following Fujitsu
Computers sensor chips, including support for the integrated Siemens Computers (FSC) sensor chips: Poseidon, Scylla, Hermes,
watchdog. Heimdall, Heracles, Hades and Syleus including support for the
integrated watchdog.
This is a merged driver for FSC sensor chips replacing the fscpos, This is a merged driver for FSC sensor chips replacing the fscpos,
fscscy and fscher drivers and adding support for several other FSC fscscy and fscher drivers and adding support for several other FSC
...@@ -635,6 +636,20 @@ config SENSORS_PC87427 ...@@ -635,6 +636,20 @@ config SENSORS_PC87427
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.
config SENSORS_PCF8591
tristate "Philips PCF8591 ADC/DAC"
depends on I2C
default n
help
If you say yes here you get support for Philips PCF8591 4-channel
ADC, 1-channel DAC chips.
This driver can also be built as a module. If so, the module
will be called pcf8591.
These devices are hard to detect and rarely found on mainstream
hardware. If unsure, say N.
config SENSORS_SIS5595 config SENSORS_SIS5595
tristate "Silicon Integrated Systems Corp. SiS5595" tristate "Silicon Integrated Systems Corp. SiS5595"
depends on PCI depends on PCI
...@@ -827,7 +842,7 @@ config SENSORS_W83627HF ...@@ -827,7 +842,7 @@ config SENSORS_W83627HF
will be called w83627hf. will be called w83627hf.
config SENSORS_W83627EHF config SENSORS_W83627EHF
tristate "Winbond W83627EHF/DHG" tristate "Winbond W83627EHF/EHG/DHG, W83667HG"
select HWMON_VID select HWMON_VID
help help
If you say yes here you get support for the hardware If you say yes here you get support for the hardware
...@@ -838,6 +853,8 @@ config SENSORS_W83627EHF ...@@ -838,6 +853,8 @@ config SENSORS_W83627EHF
chip suited for specific Intel processors that use PECI such as chip suited for specific Intel processors that use PECI such as
the Core 2 Duo. the Core 2 Duo.
This driver also supports the W83667HG chip.
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called w83627ehf. will be called w83627ehf.
......
...@@ -70,6 +70,7 @@ obj-$(CONFIG_SENSORS_MAX1619) += max1619.o ...@@ -70,6 +70,7 @@ obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o
obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
......
...@@ -81,71 +81,84 @@ struct ds1621_data { ...@@ -81,71 +81,84 @@ struct ds1621_data {
u8 conf; /* Register encoding, combined */ u8 conf; /* Register encoding, combined */
}; };
static int ds1621_probe(struct i2c_client *client, /* Temperature registers are word-sized.
const struct i2c_device_id *id);
static int ds1621_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info);
static void ds1621_init_client(struct i2c_client *client);
static int ds1621_remove(struct i2c_client *client);
static struct ds1621_data *ds1621_update_client(struct device *dev);
static const struct i2c_device_id ds1621_id[] = {
{ "ds1621", ds1621 },
{ "ds1625", ds1621 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ds1621_id);
/* This is the driver that will be inserted */
static struct i2c_driver ds1621_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "ds1621",
},
.probe = ds1621_probe,
.remove = ds1621_remove,
.id_table = ds1621_id,
.detect = ds1621_detect,
.address_data = &addr_data,
};
/* All registers are word-sized, except for the configuration register.
DS1621 uses a high-byte first convention, which is exactly opposite to DS1621 uses a high-byte first convention, which is exactly opposite to
the SMBus standard. */ the SMBus standard. */
static int ds1621_read_value(struct i2c_client *client, u8 reg) static int ds1621_read_temp(struct i2c_client *client, u8 reg)
{ {
if (reg == DS1621_REG_CONF) int ret;
return i2c_smbus_read_byte_data(client, reg);
else ret = i2c_smbus_read_word_data(client, reg);
return swab16(i2c_smbus_read_word_data(client, reg)); if (ret < 0)
return ret;
return swab16(ret);
} }
static int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value) static int ds1621_write_temp(struct i2c_client *client, u8 reg, u16 value)
{ {
if (reg == DS1621_REG_CONF) return i2c_smbus_write_word_data(client, reg, swab16(value));
return i2c_smbus_write_byte_data(client, reg, value);
else
return i2c_smbus_write_word_data(client, reg, swab16(value));
} }
static void ds1621_init_client(struct i2c_client *client) static void ds1621_init_client(struct i2c_client *client)
{ {
int reg = ds1621_read_value(client, DS1621_REG_CONF); u8 conf, new_conf;
new_conf = conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF);
/* switch to continuous conversion mode */ /* switch to continuous conversion mode */
reg &= ~ DS1621_REG_CONFIG_1SHOT; new_conf &= ~DS1621_REG_CONFIG_1SHOT;
/* setup output polarity */ /* setup output polarity */
if (polarity == 0) if (polarity == 0)
reg &= ~DS1621_REG_CONFIG_POLARITY; new_conf &= ~DS1621_REG_CONFIG_POLARITY;
else if (polarity == 1) else if (polarity == 1)
reg |= DS1621_REG_CONFIG_POLARITY; new_conf |= DS1621_REG_CONFIG_POLARITY;
ds1621_write_value(client, DS1621_REG_CONF, reg); if (conf != new_conf)
i2c_smbus_write_byte_data(client, DS1621_REG_CONF, new_conf);
/* start conversion */ /* start conversion */
i2c_smbus_write_byte(client, DS1621_COM_START); i2c_smbus_write_byte(client, DS1621_COM_START);
} }
static struct ds1621_data *ds1621_update_client(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct ds1621_data *data = i2c_get_clientdata(client);
u8 new_conf;
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
int i;
dev_dbg(&client->dev, "Starting ds1621 update\n");
data->conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF);
for (i = 0; i < ARRAY_SIZE(data->temp); i++)
data->temp[i] = ds1621_read_temp(client,
DS1621_REG_TEMP[i]);
/* reset alarms if necessary */
new_conf = data->conf;
if (data->temp[0] > data->temp[1]) /* input > min */
new_conf &= ~DS1621_ALARM_TEMP_LOW;
if (data->temp[0] < data->temp[2]) /* input < max */
new_conf &= ~DS1621_ALARM_TEMP_HIGH;
if (data->conf != new_conf)
i2c_smbus_write_byte_data(client, DS1621_REG_CONF,
new_conf);
data->last_updated = jiffies;
data->valid = 1;
}
mutex_unlock(&data->update_lock);
return data;
}
static ssize_t show_temp(struct device *dev, struct device_attribute *da, static ssize_t show_temp(struct device *dev, struct device_attribute *da,
char *buf) char *buf)
{ {
...@@ -160,13 +173,13 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da, ...@@ -160,13 +173,13 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(da); struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = to_i2c_client(dev);
struct ds1621_data *data = ds1621_update_client(dev); struct ds1621_data *data = i2c_get_clientdata(client);
u16 val = LM75_TEMP_TO_REG(simple_strtol(buf, NULL, 10)); u16 val = LM75_TEMP_TO_REG(simple_strtol(buf, NULL, 10));
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
data->temp[attr->index] = val; data->temp[attr->index] = val;
ds1621_write_value(client, DS1621_REG_TEMP[attr->index], ds1621_write_temp(client, DS1621_REG_TEMP[attr->index],
data->temp[attr->index]); data->temp[attr->index]);
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
return count; return count;
} }
...@@ -228,13 +241,14 @@ static int ds1621_detect(struct i2c_client *client, int kind, ...@@ -228,13 +241,14 @@ static int ds1621_detect(struct i2c_client *client, int kind,
/* The NVB bit should be low if no EEPROM write has been /* The NVB bit should be low if no EEPROM write has been
requested during the latest 10ms, which is highly requested during the latest 10ms, which is highly
improbable in our case. */ improbable in our case. */
conf = ds1621_read_value(client, DS1621_REG_CONF); conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF);
if (conf & DS1621_REG_CONFIG_NVB) if (conf < 0 || conf & DS1621_REG_CONFIG_NVB)
return -ENODEV; return -ENODEV;
/* The 7 lowest bits of a temperature should always be 0. */ /* The 7 lowest bits of a temperature should always be 0. */
for (i = 0; i < ARRAY_SIZE(DS1621_REG_TEMP); i++) { for (i = 0; i < ARRAY_SIZE(DS1621_REG_TEMP); i++) {
temp = ds1621_read_value(client, DS1621_REG_TEMP[i]); temp = i2c_smbus_read_word_data(client,
if (temp & 0x007f) DS1621_REG_TEMP[i]);
if (temp < 0 || (temp & 0x7f00))
return -ENODEV; return -ENODEV;
} }
} }
...@@ -294,45 +308,25 @@ static int ds1621_remove(struct i2c_client *client) ...@@ -294,45 +308,25 @@ static int ds1621_remove(struct i2c_client *client)
return 0; return 0;
} }
static const struct i2c_device_id ds1621_id[] = {
{ "ds1621", ds1621 },
{ "ds1625", ds1621 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ds1621_id);
static struct ds1621_data *ds1621_update_client(struct device *dev) /* This is the driver that will be inserted */
{ static struct i2c_driver ds1621_driver = {
struct i2c_client *client = to_i2c_client(dev); .class = I2C_CLASS_HWMON,
struct ds1621_data *data = i2c_get_clientdata(client); .driver = {
u8 new_conf; .name = "ds1621",
},
mutex_lock(&data->update_lock); .probe = ds1621_probe,
.remove = ds1621_remove,
if (time_after(jiffies, data->last_updated + HZ + HZ / 2) .id_table = ds1621_id,
|| !data->valid) { .detect = ds1621_detect,
int i; .address_data = &addr_data,
};
dev_dbg(&client->dev, "Starting ds1621 update\n");
data->conf = ds1621_read_value(client, DS1621_REG_CONF);
for (i = 0; i < ARRAY_SIZE(data->temp); i++)
data->temp[i] = ds1621_read_value(client,
DS1621_REG_TEMP[i]);
/* reset alarms if necessary */
new_conf = data->conf;
if (data->temp[0] > data->temp[1]) /* input > min */
new_conf &= ~DS1621_ALARM_TEMP_LOW;
if (data->temp[0] < data->temp[2]) /* input < max */
new_conf &= ~DS1621_ALARM_TEMP_HIGH;
if (data->conf != new_conf)
ds1621_write_value(client, DS1621_REG_CONF,
new_conf);
data->last_updated = jiffies;
data->valid = 1;
}
mutex_unlock(&data->update_lock);
return data;
}
static int __init ds1621_init(void) static int __init ds1621_init(void)
{ {
......
/* fschmd.c /* fschmd.c
* *
* Copyright (C) 2007,2008 Hans de Goede <hdegoede@redhat.com> * Copyright (C) 2007 - 2009 Hans de Goede <hdegoede@redhat.com>
* *
* 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 as published by * it under the terms of the GNU General Public License as published by
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
/* /*
* Merged Fujitsu Siemens hwmon driver, supporting the Poseidon, Hermes, * Merged Fujitsu Siemens hwmon driver, supporting the Poseidon, Hermes,
* Scylla, Heracles and Heimdall chips * Scylla, Heracles, Heimdall, Hades and Syleus chips
* *
* Based on the original 2.4 fscscy, 2.6 fscpos, 2.6 fscher and 2.6 * Based on the original 2.4 fscscy, 2.6 fscpos, 2.6 fscher and 2.6
* (candidate) fschmd drivers: * (candidate) fschmd drivers:
...@@ -56,7 +56,7 @@ static int nowayout = WATCHDOG_NOWAYOUT; ...@@ -56,7 +56,7 @@ static int nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, int, 0); module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd); I2C_CLIENT_INSMOD_7(fscpos, fscher, fscscy, fschrc, fschmd, fschds, fscsyl);
/* /*
* The FSCHMD registers and other defines * The FSCHMD registers and other defines
...@@ -75,9 +75,12 @@ I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd); ...@@ -75,9 +75,12 @@ I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd);
#define FSCHMD_CONTROL_ALERT_LED 0x01 #define FSCHMD_CONTROL_ALERT_LED 0x01
/* watchdog */ /* watchdog */
#define FSCHMD_REG_WDOG_PRESET 0x28 static const u8 FSCHMD_REG_WDOG_CONTROL[7] =
#define FSCHMD_REG_WDOG_STATE 0x23 { 0x21, 0x21, 0x21, 0x21, 0x21, 0x28, 0x28 };
#define FSCHMD_REG_WDOG_CONTROL 0x21 static const u8 FSCHMD_REG_WDOG_STATE[7] =
{ 0x23, 0x23, 0x23, 0x23, 0x23, 0x29, 0x29 };
static const u8 FSCHMD_REG_WDOG_PRESET[7] =
{ 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x2a };
#define FSCHMD_WDOG_CONTROL_TRIGGER 0x10 #define FSCHMD_WDOG_CONTROL_TRIGGER 0x10
#define FSCHMD_WDOG_CONTROL_STARTED 0x10 /* the same as trigger */ #define FSCHMD_WDOG_CONTROL_STARTED 0x10 /* the same as trigger */
...@@ -87,70 +90,95 @@ I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd); ...@@ -87,70 +90,95 @@ I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd);
#define FSCHMD_WDOG_STATE_CARDRESET 0x02 #define FSCHMD_WDOG_STATE_CARDRESET 0x02
/* voltages, weird order is to keep the same order as the old drivers */ /* voltages, weird order is to keep the same order as the old drivers */
static const u8 FSCHMD_REG_VOLT[3] = { 0x45, 0x42, 0x48 }; static const u8 FSCHMD_REG_VOLT[7][6] = {
{ 0x45, 0x42, 0x48 }, /* pos */
{ 0x45, 0x42, 0x48 }, /* her */
{ 0x45, 0x42, 0x48 }, /* scy */
{ 0x45, 0x42, 0x48 }, /* hrc */
{ 0x45, 0x42, 0x48 }, /* hmd */
{ 0x21, 0x20, 0x22 }, /* hds */
{ 0x21, 0x20, 0x22, 0x23, 0x24, 0x25 }, /* syl */
};
static const int FSCHMD_NO_VOLT_SENSORS[7] = { 3, 3, 3, 3, 3, 3, 6 };
/* minimum pwm at which the fan is driven (pwm can by increased depending on /* minimum pwm at which the fan is driven (pwm can by increased depending on
the temp. Notice that for the scy some fans share there minimum speed. the temp. Notice that for the scy some fans share there minimum speed.
Also notice that with the scy the sensor order is different than with the Also notice that with the scy the sensor order is different than with the
other chips, this order was in the 2.4 driver and kept for consistency. */ other chips, this order was in the 2.4 driver and kept for consistency. */
static const u8 FSCHMD_REG_FAN_MIN[5][6] = { static const u8 FSCHMD_REG_FAN_MIN[7][7] = {
{ 0x55, 0x65 }, /* pos */ { 0x55, 0x65 }, /* pos */
{ 0x55, 0x65, 0xb5 }, /* her */ { 0x55, 0x65, 0xb5 }, /* her */
{ 0x65, 0x65, 0x55, 0xa5, 0x55, 0xa5 }, /* scy */ { 0x65, 0x65, 0x55, 0xa5, 0x55, 0xa5 }, /* scy */
{ 0x55, 0x65, 0xa5, 0xb5 }, /* hrc */ { 0x55, 0x65, 0xa5, 0xb5 }, /* hrc */
{ 0x55, 0x65, 0xa5, 0xb5, 0xc5 }, /* hmd */ { 0x55, 0x65, 0xa5, 0xb5, 0xc5 }, /* hmd */
{ 0x55, 0x65, 0xa5, 0xb5, 0xc5 }, /* hds */
{ 0x54, 0x64, 0x74, 0x84, 0x94, 0xa4, 0xb4 }, /* syl */
}; };
/* actual fan speed */ /* actual fan speed */
static const u8 FSCHMD_REG_FAN_ACT[5][6] = { static const u8 FSCHMD_REG_FAN_ACT[7][7] = {
{ 0x0e, 0x6b, 0xab }, /* pos */ { 0x0e, 0x6b, 0xab }, /* pos */
{ 0x0e, 0x6b, 0xbb }, /* her */ { 0x0e, 0x6b, 0xbb }, /* her */
{ 0x6b, 0x6c, 0x0e, 0xab, 0x5c, 0xbb }, /* scy */ { 0x6b, 0x6c, 0x0e, 0xab, 0x5c, 0xbb }, /* scy */
{ 0x0e, 0x6b, 0xab, 0xbb }, /* hrc */ { 0x0e, 0x6b, 0xab, 0xbb }, /* hrc */
{ 0x5b, 0x6b, 0xab, 0xbb, 0xcb }, /* hmd */ { 0x5b, 0x6b, 0xab, 0xbb, 0xcb }, /* hmd */
{ 0x5b, 0x6b, 0xab, 0xbb, 0xcb }, /* hds */
{ 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7 }, /* syl */
}; };
/* fan status registers */ /* fan status registers */
static const u8 FSCHMD_REG_FAN_STATE[5][6] = { static const u8 FSCHMD_REG_FAN_STATE[7][7] = {
{ 0x0d, 0x62, 0xa2 }, /* pos */ { 0x0d, 0x62, 0xa2 }, /* pos */
{ 0x0d, 0x62, 0xb2 }, /* her */ { 0x0d, 0x62, 0xb2 }, /* her */
{ 0x62, 0x61, 0x0d, 0xa2, 0x52, 0xb2 }, /* scy */ { 0x62, 0x61, 0x0d, 0xa2, 0x52, 0xb2 }, /* scy */
{ 0x0d, 0x62, 0xa2, 0xb2 }, /* hrc */ { 0x0d, 0x62, 0xa2, 0xb2 }, /* hrc */
{ 0x52, 0x62, 0xa2, 0xb2, 0xc2 }, /* hmd */ { 0x52, 0x62, 0xa2, 0xb2, 0xc2 }, /* hmd */
{ 0x52, 0x62, 0xa2, 0xb2, 0xc2 }, /* hds */
{ 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0 }, /* syl */
}; };
/* fan ripple / divider registers */ /* fan ripple / divider registers */
static const u8 FSCHMD_REG_FAN_RIPPLE[5][6] = { static const u8 FSCHMD_REG_FAN_RIPPLE[7][7] = {
{ 0x0f, 0x6f, 0xaf }, /* pos */ { 0x0f, 0x6f, 0xaf }, /* pos */
{ 0x0f, 0x6f, 0xbf }, /* her */ { 0x0f, 0x6f, 0xbf }, /* her */
{ 0x6f, 0x6f, 0x0f, 0xaf, 0x0f, 0xbf }, /* scy */ { 0x6f, 0x6f, 0x0f, 0xaf, 0x0f, 0xbf }, /* scy */
{ 0x0f, 0x6f, 0xaf, 0xbf }, /* hrc */ { 0x0f, 0x6f, 0xaf, 0xbf }, /* hrc */
{ 0x5f, 0x6f, 0xaf, 0xbf, 0xcf }, /* hmd */ { 0x5f, 0x6f, 0xaf, 0xbf, 0xcf }, /* hmd */
{ 0x5f, 0x6f, 0xaf, 0xbf, 0xcf }, /* hds */
{ 0x56, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6 }, /* syl */
}; };
static const int FSCHMD_NO_FAN_SENSORS[5] = { 3, 3, 6, 4, 5 }; static const int FSCHMD_NO_FAN_SENSORS[7] = { 3, 3, 6, 4, 5, 5, 7 };
/* Fan status register bitmasks */ /* Fan status register bitmasks */
#define FSCHMD_FAN_ALARM 0x04 /* called fault by FSC! */ #define FSCHMD_FAN_ALARM 0x04 /* called fault by FSC! */
#define FSCHMD_FAN_NOT_PRESENT 0x08 /* not documented */ #define FSCHMD_FAN_NOT_PRESENT 0x08
#define FSCHMD_FAN_DISABLED 0x80
/* actual temperature registers */ /* actual temperature registers */
static const u8 FSCHMD_REG_TEMP_ACT[5][5] = { static const u8 FSCHMD_REG_TEMP_ACT[7][11] = {
{ 0x64, 0x32, 0x35 }, /* pos */ { 0x64, 0x32, 0x35 }, /* pos */
{ 0x64, 0x32, 0x35 }, /* her */ { 0x64, 0x32, 0x35 }, /* her */
{ 0x64, 0xD0, 0x32, 0x35 }, /* scy */ { 0x64, 0xD0, 0x32, 0x35 }, /* scy */
{ 0x64, 0x32, 0x35 }, /* hrc */ { 0x64, 0x32, 0x35 }, /* hrc */
{ 0x70, 0x80, 0x90, 0xd0, 0xe0 }, /* hmd */ { 0x70, 0x80, 0x90, 0xd0, 0xe0 }, /* hmd */
{ 0x70, 0x80, 0x90, 0xd0, 0xe0 }, /* hds */
{ 0x58, 0x68, 0x78, 0x88, 0x98, 0xa8, /* syl */
0xb8, 0xc8, 0xd8, 0xe8, 0xf8 },
}; };
/* temperature state registers */ /* temperature state registers */
static const u8 FSCHMD_REG_TEMP_STATE[5][5] = { static const u8 FSCHMD_REG_TEMP_STATE[7][11] = {
{ 0x71, 0x81, 0x91 }, /* pos */ { 0x71, 0x81, 0x91 }, /* pos */
{ 0x71, 0x81, 0x91 }, /* her */ { 0x71, 0x81, 0x91 }, /* her */
{ 0x71, 0xd1, 0x81, 0x91 }, /* scy */ { 0x71, 0xd1, 0x81, 0x91 }, /* scy */
{ 0x71, 0x81, 0x91 }, /* hrc */ { 0x71, 0x81, 0x91 }, /* hrc */
{ 0x71, 0x81, 0x91, 0xd1, 0xe1 }, /* hmd */ { 0x71, 0x81, 0x91, 0xd1, 0xe1 }, /* hmd */
{ 0x71, 0x81, 0x91, 0xd1, 0xe1 }, /* hds */
{ 0x59, 0x69, 0x79, 0x89, 0x99, 0xa9, /* syl */
0xb9, 0xc9, 0xd9, 0xe9, 0xf9 },
}; };
/* temperature high limit registers, FSC does not document these. Proven to be /* temperature high limit registers, FSC does not document these. Proven to be
...@@ -158,24 +186,31 @@ static const u8 FSCHMD_REG_TEMP_STATE[5][5] = { ...@@ -158,24 +186,31 @@ static const u8 FSCHMD_REG_TEMP_STATE[5][5] = {
in the fscscy 2.4 driver. FSC has confirmed that the fschmd has registers in the fscscy 2.4 driver. FSC has confirmed that the fschmd has registers
at these addresses, but doesn't want to confirm they are the same as with at these addresses, but doesn't want to confirm they are the same as with
the fscher?? */ the fscher?? */
static const u8 FSCHMD_REG_TEMP_LIMIT[5][5] = { static const u8 FSCHMD_REG_TEMP_LIMIT[7][11] = {
{ 0, 0, 0 }, /* pos */ { 0, 0, 0 }, /* pos */
{ 0x76, 0x86, 0x96 }, /* her */ { 0x76, 0x86, 0x96 }, /* her */
{ 0x76, 0xd6, 0x86, 0x96 }, /* scy */ { 0x76, 0xd6, 0x86, 0x96 }, /* scy */
{ 0x76, 0x86, 0x96 }, /* hrc */ { 0x76, 0x86, 0x96 }, /* hrc */
{ 0x76, 0x86, 0x96, 0xd6, 0xe6 }, /* hmd */ { 0x76, 0x86, 0x96, 0xd6, 0xe6 }, /* hmd */
{ 0x76, 0x86, 0x96, 0xd6, 0xe6 }, /* hds */
{ 0x5a, 0x6a, 0x7a, 0x8a, 0x9a, 0xaa, /* syl */
0xba, 0xca, 0xda, 0xea, 0xfa },
}; };
/* These were found through experimenting with an fscher, currently they are /* These were found through experimenting with an fscher, currently they are
not used, but we keep them around for future reference. not used, but we keep them around for future reference.
On the fscsyl AUTOP1 lives at 0x#c (so 0x5c for fan1, 0x6c for fan2, etc),
AUTOP2 lives at 0x#e, and 0x#1 is a bitmask defining which temps influence
the fan speed.
static const u8 FSCHER_REG_TEMP_AUTOP1[] = { 0x73, 0x83, 0x93 }; static const u8 FSCHER_REG_TEMP_AUTOP1[] = { 0x73, 0x83, 0x93 };
static const u8 FSCHER_REG_TEMP_AUTOP2[] = { 0x75, 0x85, 0x95 }; */ static const u8 FSCHER_REG_TEMP_AUTOP2[] = { 0x75, 0x85, 0x95 }; */
static const int FSCHMD_NO_TEMP_SENSORS[5] = { 3, 3, 4, 3, 5 }; static const int FSCHMD_NO_TEMP_SENSORS[7] = { 3, 3, 4, 3, 5, 5, 11 };
/* temp status register bitmasks */ /* temp status register bitmasks */
#define FSCHMD_TEMP_WORKING 0x01 #define FSCHMD_TEMP_WORKING 0x01
#define FSCHMD_TEMP_ALERT 0x02 #define FSCHMD_TEMP_ALERT 0x02
#define FSCHMD_TEMP_DISABLED 0x80
/* there only really is an alarm if the sensor is working and alert == 1 */ /* there only really is an alarm if the sensor is working and alert == 1 */
#define FSCHMD_TEMP_ALARM_MASK \ #define FSCHMD_TEMP_ALARM_MASK \
(FSCHMD_TEMP_WORKING | FSCHMD_TEMP_ALERT) (FSCHMD_TEMP_WORKING | FSCHMD_TEMP_ALERT)
...@@ -201,6 +236,8 @@ static const struct i2c_device_id fschmd_id[] = { ...@@ -201,6 +236,8 @@ static const struct i2c_device_id fschmd_id[] = {
{ "fscscy", fscscy }, { "fscscy", fscscy },
{ "fschrc", fschrc }, { "fschrc", fschrc },
{ "fschmd", fschmd }, { "fschmd", fschmd },
{ "fschds", fschds },
{ "fscsyl", fscsyl },
{ } { }
}; };
MODULE_DEVICE_TABLE(i2c, fschmd_id); MODULE_DEVICE_TABLE(i2c, fschmd_id);
...@@ -242,14 +279,14 @@ struct fschmd_data { ...@@ -242,14 +279,14 @@ struct fschmd_data {
u8 watchdog_control; /* watchdog control register */ u8 watchdog_control; /* watchdog control register */
u8 watchdog_state; /* watchdog status register */ u8 watchdog_state; /* watchdog status register */
u8 watchdog_preset; /* watchdog counter preset on trigger val */ u8 watchdog_preset; /* watchdog counter preset on trigger val */
u8 volt[3]; /* 12, 5, battery voltage */ u8 volt[6]; /* voltage */
u8 temp_act[5]; /* temperature */ u8 temp_act[11]; /* temperature */
u8 temp_status[5]; /* status of sensor */ u8 temp_status[11]; /* status of sensor */
u8 temp_max[5]; /* high temp limit, notice: undocumented! */ u8 temp_max[11]; /* high temp limit, notice: undocumented! */
u8 fan_act[6]; /* fans revolutions per second */ u8 fan_act[7]; /* fans revolutions per second */
u8 fan_status[6]; /* fan status */ u8 fan_status[7]; /* fan status */
u8 fan_min[6]; /* fan min value for rps */ u8 fan_min[7]; /* fan min value for rps */
u8 fan_ripple[6]; /* divider for rps */ u8 fan_ripple[7]; /* divider for rps */
}; };
/* Global variables to hold information read from special DMI tables, which are /* Global variables to hold information read from special DMI tables, which are
...@@ -257,8 +294,8 @@ struct fschmd_data { ...@@ -257,8 +294,8 @@ struct fschmd_data {
protect these with a lock as they are only modified from our attach function protect these with a lock as they are only modified from our attach function
which always gets called with the i2c-core lock held and never accessed which always gets called with the i2c-core lock held and never accessed
before the attach function is done with them. */ before the attach function is done with them. */
static int dmi_mult[3] = { 490, 200, 100 }; static int dmi_mult[6] = { 490, 200, 100, 100, 200, 100 };
static int dmi_offset[3] = { 0, 0, 0 }; static int dmi_offset[6] = { 0, 0, 0, 0, 0, 0 };
static int dmi_vref = -1; static int dmi_vref = -1;
/* Somewhat ugly :( global data pointer list with all fschmd devices, so that /* Somewhat ugly :( global data pointer list with all fschmd devices, so that
...@@ -450,10 +487,11 @@ static ssize_t show_pwm_auto_point1_pwm(struct device *dev, ...@@ -450,10 +487,11 @@ static ssize_t show_pwm_auto_point1_pwm(struct device *dev,
struct device_attribute *devattr, char *buf) struct device_attribute *devattr, char *buf)
{ {
int index = to_sensor_dev_attr(devattr)->index; int index = to_sensor_dev_attr(devattr)->index;
int val = fschmd_update_device(dev)->fan_min[index]; struct fschmd_data *data = fschmd_update_device(dev);
int val = data->fan_min[index];
/* 0 = allow turning off, 1-255 = 50-100% */ /* 0 = allow turning off (except on the syl), 1-255 = 50-100% */
if (val) if (val || data->kind == fscsyl - 1)
val = val / 2 + 128; val = val / 2 + 128;
return sprintf(buf, "%d\n", val); return sprintf(buf, "%d\n", val);
...@@ -466,8 +504,8 @@ static ssize_t store_pwm_auto_point1_pwm(struct device *dev, ...@@ -466,8 +504,8 @@ static ssize_t store_pwm_auto_point1_pwm(struct device *dev,
struct fschmd_data *data = dev_get_drvdata(dev); struct fschmd_data *data = dev_get_drvdata(dev);
unsigned long v = simple_strtoul(buf, NULL, 10); unsigned long v = simple_strtoul(buf, NULL, 10);
/* register: 0 = allow turning off, 1-255 = 50-100% */ /* reg: 0 = allow turning off (except on the syl), 1-255 = 50-100% */
if (v) { if (v || data->kind == fscsyl - 1) {
v = SENSORS_LIMIT(v, 128, 255); v = SENSORS_LIMIT(v, 128, 255);
v = (v - 128) * 2 + 1; v = (v - 128) * 2 + 1;
} }
...@@ -522,11 +560,15 @@ static ssize_t store_alert_led(struct device *dev, ...@@ -522,11 +560,15 @@ static ssize_t store_alert_led(struct device *dev,
return count; return count;
} }
static DEVICE_ATTR(alert_led, 0644, show_alert_led, store_alert_led);
static struct sensor_device_attribute fschmd_attr[] = { static struct sensor_device_attribute fschmd_attr[] = {
SENSOR_ATTR(in0_input, 0444, show_in_value, NULL, 0), SENSOR_ATTR(in0_input, 0444, show_in_value, NULL, 0),
SENSOR_ATTR(in1_input, 0444, show_in_value, NULL, 1), SENSOR_ATTR(in1_input, 0444, show_in_value, NULL, 1),
SENSOR_ATTR(in2_input, 0444, show_in_value, NULL, 2), SENSOR_ATTR(in2_input, 0444, show_in_value, NULL, 2),
SENSOR_ATTR(alert_led, 0644, show_alert_led, store_alert_led, 0), SENSOR_ATTR(in3_input, 0444, show_in_value, NULL, 3),
SENSOR_ATTR(in4_input, 0444, show_in_value, NULL, 4),
SENSOR_ATTR(in5_input, 0444, show_in_value, NULL, 5),
}; };
static struct sensor_device_attribute fschmd_temp_attr[] = { static struct sensor_device_attribute fschmd_temp_attr[] = {
...@@ -550,6 +592,30 @@ static struct sensor_device_attribute fschmd_temp_attr[] = { ...@@ -550,6 +592,30 @@ static struct sensor_device_attribute fschmd_temp_attr[] = {
SENSOR_ATTR(temp5_max, 0644, show_temp_max, store_temp_max, 4), SENSOR_ATTR(temp5_max, 0644, show_temp_max, store_temp_max, 4),
SENSOR_ATTR(temp5_fault, 0444, show_temp_fault, NULL, 4), SENSOR_ATTR(temp5_fault, 0444, show_temp_fault, NULL, 4),
SENSOR_ATTR(temp5_alarm, 0444, show_temp_alarm, NULL, 4), SENSOR_ATTR(temp5_alarm, 0444, show_temp_alarm, NULL, 4),
SENSOR_ATTR(temp6_input, 0444, show_temp_value, NULL, 5),
SENSOR_ATTR(temp6_max, 0644, show_temp_max, store_temp_max, 5),
SENSOR_ATTR(temp6_fault, 0444, show_temp_fault, NULL, 5),
SENSOR_ATTR(temp6_alarm, 0444, show_temp_alarm, NULL, 5),
SENSOR_ATTR(temp7_input, 0444, show_temp_value, NULL, 6),
SENSOR_ATTR(temp7_max, 0644, show_temp_max, store_temp_max, 6),
SENSOR_ATTR(temp7_fault, 0444, show_temp_fault, NULL, 6),
SENSOR_ATTR(temp7_alarm, 0444, show_temp_alarm, NULL, 6),
SENSOR_ATTR(temp8_input, 0444, show_temp_value, NULL, 7),
SENSOR_ATTR(temp8_max, 0644, show_temp_max, store_temp_max, 7),
SENSOR_ATTR(temp8_fault, 0444, show_temp_fault, NULL, 7),
SENSOR_ATTR(temp8_alarm, 0444, show_temp_alarm, NULL, 7),
SENSOR_ATTR(temp9_input, 0444, show_temp_value, NULL, 8),
SENSOR_ATTR(temp9_max, 0644, show_temp_max, store_temp_max, 8),
SENSOR_ATTR(temp9_fault, 0444, show_temp_fault, NULL, 8),
SENSOR_ATTR(temp9_alarm, 0444, show_temp_alarm, NULL, 8),
SENSOR_ATTR(temp10_input, 0444, show_temp_value, NULL, 9),
SENSOR_ATTR(temp10_max, 0644, show_temp_max, store_temp_max, 9),
SENSOR_ATTR(temp10_fault, 0444, show_temp_fault, NULL, 9),
SENSOR_ATTR(temp10_alarm, 0444, show_temp_alarm, NULL, 9),
SENSOR_ATTR(temp11_input, 0444, show_temp_value, NULL, 10),
SENSOR_ATTR(temp11_max, 0644, show_temp_max, store_temp_max, 10),
SENSOR_ATTR(temp11_fault, 0444, show_temp_fault, NULL, 10),
SENSOR_ATTR(temp11_alarm, 0444, show_temp_alarm, NULL, 10),
}; };
static struct sensor_device_attribute fschmd_fan_attr[] = { static struct sensor_device_attribute fschmd_fan_attr[] = {
...@@ -589,6 +655,12 @@ static struct sensor_device_attribute fschmd_fan_attr[] = { ...@@ -589,6 +655,12 @@ static struct sensor_device_attribute fschmd_fan_attr[] = {
SENSOR_ATTR(fan6_fault, 0444, show_fan_fault, NULL, 5), SENSOR_ATTR(fan6_fault, 0444, show_fan_fault, NULL, 5),
SENSOR_ATTR(pwm6_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm, SENSOR_ATTR(pwm6_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm,
store_pwm_auto_point1_pwm, 5), store_pwm_auto_point1_pwm, 5),
SENSOR_ATTR(fan7_input, 0444, show_fan_value, NULL, 6),
SENSOR_ATTR(fan7_div, 0644, show_fan_div, store_fan_div, 6),
SENSOR_ATTR(fan7_alarm, 0444, show_fan_alarm, NULL, 6),
SENSOR_ATTR(fan7_fault, 0444, show_fan_fault, NULL, 6),
SENSOR_ATTR(pwm7_auto_point1_pwm, 0644, show_pwm_auto_point1_pwm,
store_pwm_auto_point1_pwm, 6),
}; };
...@@ -624,10 +696,11 @@ static int watchdog_set_timeout(struct fschmd_data *data, int timeout) ...@@ -624,10 +696,11 @@ static int watchdog_set_timeout(struct fschmd_data *data, int timeout)
data->watchdog_preset = DIV_ROUND_UP(timeout, resolution); data->watchdog_preset = DIV_ROUND_UP(timeout, resolution);
/* Write new timeout value */ /* Write new timeout value */
i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_PRESET, i2c_smbus_write_byte_data(data->client,
data->watchdog_preset); FSCHMD_REG_WDOG_PRESET[data->kind], data->watchdog_preset);
/* Write new control register, do not trigger! */ /* Write new control register, do not trigger! */
i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_CONTROL, i2c_smbus_write_byte_data(data->client,
FSCHMD_REG_WDOG_CONTROL[data->kind],
data->watchdog_control & ~FSCHMD_WDOG_CONTROL_TRIGGER); data->watchdog_control & ~FSCHMD_WDOG_CONTROL_TRIGGER);
ret = data->watchdog_preset * resolution; ret = data->watchdog_preset * resolution;
...@@ -662,8 +735,9 @@ static int watchdog_trigger(struct fschmd_data *data) ...@@ -662,8 +735,9 @@ static int watchdog_trigger(struct fschmd_data *data)
} }
data->watchdog_control |= FSCHMD_WDOG_CONTROL_TRIGGER; data->watchdog_control |= FSCHMD_WDOG_CONTROL_TRIGGER;
i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_CONTROL, i2c_smbus_write_byte_data(data->client,
data->watchdog_control); FSCHMD_REG_WDOG_CONTROL[data->kind],
data->watchdog_control);
leave: leave:
mutex_unlock(&data->watchdog_lock); mutex_unlock(&data->watchdog_lock);
return ret; return ret;
...@@ -682,7 +756,8 @@ static int watchdog_stop(struct fschmd_data *data) ...@@ -682,7 +756,8 @@ static int watchdog_stop(struct fschmd_data *data)
data->watchdog_control &= ~FSCHMD_WDOG_CONTROL_STARTED; data->watchdog_control &= ~FSCHMD_WDOG_CONTROL_STARTED;
/* Don't store the stop flag in our watchdog control register copy, as /* Don't store the stop flag in our watchdog control register copy, as
its a write only bit (read always returns 0) */ its a write only bit (read always returns 0) */
i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_CONTROL, i2c_smbus_write_byte_data(data->client,
FSCHMD_REG_WDOG_CONTROL[data->kind],
data->watchdog_control | FSCHMD_WDOG_CONTROL_STOP); data->watchdog_control | FSCHMD_WDOG_CONTROL_STOP);
leave: leave:
mutex_unlock(&data->watchdog_lock); mutex_unlock(&data->watchdog_lock);
...@@ -856,7 +931,7 @@ static struct file_operations watchdog_fops = { ...@@ -856,7 +931,7 @@ static struct file_operations watchdog_fops = {
/* DMI decode routine to read voltage scaling factors from special DMI tables, /* DMI decode routine to read voltage scaling factors from special DMI tables,
which are available on FSC machines with an fscher or later chip. */ which are available on FSC machines with an fscher or later chip. */
static void fschmd_dmi_decode(const struct dmi_header *header) static void fschmd_dmi_decode(const struct dmi_header *header, void *dummy)
{ {
int i, mult[3] = { 0 }, offset[3] = { 0 }, vref = 0, found = 0; int i, mult[3] = { 0 }, offset[3] = { 0 }, vref = 0, found = 0;
...@@ -912,6 +987,15 @@ static void fschmd_dmi_decode(const struct dmi_header *header) ...@@ -912,6 +987,15 @@ static void fschmd_dmi_decode(const struct dmi_header *header)
dmi_mult[i] = mult[i] * 10; dmi_mult[i] = mult[i] * 10;
dmi_offset[i] = offset[i] * 10; dmi_offset[i] = offset[i] * 10;
} }
/* According to the docs there should be separate dmi entries
for the mult's and offsets of in3-5 of the syl, but on
my test machine these are not present */
dmi_mult[3] = dmi_mult[2];
dmi_mult[4] = dmi_mult[1];
dmi_mult[5] = dmi_mult[2];
dmi_offset[3] = dmi_offset[2];
dmi_offset[4] = dmi_offset[1];
dmi_offset[5] = dmi_offset[2];
dmi_vref = vref; dmi_vref = vref;
} }
} }
...@@ -920,8 +1004,6 @@ static int fschmd_detect(struct i2c_client *client, int kind, ...@@ -920,8 +1004,6 @@ static int fschmd_detect(struct i2c_client *client, int kind,
struct i2c_board_info *info) struct i2c_board_info *info)
{ {
struct i2c_adapter *adapter = client->adapter; struct i2c_adapter *adapter = client->adapter;
const char * const client_names[5] = { "fscpos", "fscher", "fscscy",
"fschrc", "fschmd" };
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV; return -ENODEV;
...@@ -948,11 +1030,15 @@ static int fschmd_detect(struct i2c_client *client, int kind, ...@@ -948,11 +1030,15 @@ static int fschmd_detect(struct i2c_client *client, int kind,
kind = fschrc; kind = fschrc;
else if (!strcmp(id, "HMD")) else if (!strcmp(id, "HMD"))
kind = fschmd; kind = fschmd;
else if (!strcmp(id, "HDS"))
kind = fschds;
else if (!strcmp(id, "SYL"))
kind = fscsyl;
else else
return -ENODEV; return -ENODEV;
} }
strlcpy(info->type, client_names[kind - 1], I2C_NAME_SIZE); strlcpy(info->type, fschmd_id[kind - 1].name, I2C_NAME_SIZE);
return 0; return 0;
} }
...@@ -961,8 +1047,8 @@ static int fschmd_probe(struct i2c_client *client, ...@@ -961,8 +1047,8 @@ static int fschmd_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct fschmd_data *data; struct fschmd_data *data;
const char * const names[5] = { "Poseidon", "Hermes", "Scylla", const char * const names[7] = { "Poseidon", "Hermes", "Scylla",
"Heracles", "Heimdall" }; "Heracles", "Heimdall", "Hades", "Syleus" };
const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 }; const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 };
int i, err; int i, err;
enum chips kind = id->driver_data; enum chips kind = id->driver_data;
...@@ -991,7 +1077,7 @@ static int fschmd_probe(struct i2c_client *client, ...@@ -991,7 +1077,7 @@ static int fschmd_probe(struct i2c_client *client,
/* Read the special DMI table for fscher and newer chips */ /* Read the special DMI table for fscher and newer chips */
if ((kind == fscher || kind >= fschrc) && dmi_vref == -1) { if ((kind == fscher || kind >= fschrc) && dmi_vref == -1) {
dmi_walk(fschmd_dmi_decode); dmi_walk(fschmd_dmi_decode, NULL);
if (dmi_vref == -1) { if (dmi_vref == -1) {
dev_warn(&client->dev, dev_warn(&client->dev,
"Couldn't get voltage scaling factors from " "Couldn't get voltage scaling factors from "
...@@ -1000,21 +1086,25 @@ static int fschmd_probe(struct i2c_client *client, ...@@ -1000,21 +1086,25 @@ static int fschmd_probe(struct i2c_client *client,
} }
} }
/* i2c kind goes from 1-6, we want from 0-5 to address arrays */
data->kind = kind - 1;
/* Read in some never changing registers */ /* Read in some never changing registers */
data->revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION); data->revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION);
data->global_control = i2c_smbus_read_byte_data(client, data->global_control = i2c_smbus_read_byte_data(client,
FSCHMD_REG_CONTROL); FSCHMD_REG_CONTROL);
data->watchdog_control = i2c_smbus_read_byte_data(client, data->watchdog_control = i2c_smbus_read_byte_data(client,
FSCHMD_REG_WDOG_CONTROL); FSCHMD_REG_WDOG_CONTROL[data->kind]);
data->watchdog_state = i2c_smbus_read_byte_data(client, data->watchdog_state = i2c_smbus_read_byte_data(client,
FSCHMD_REG_WDOG_STATE); FSCHMD_REG_WDOG_STATE[data->kind]);
data->watchdog_preset = i2c_smbus_read_byte_data(client, data->watchdog_preset = i2c_smbus_read_byte_data(client,
FSCHMD_REG_WDOG_PRESET); FSCHMD_REG_WDOG_PRESET[data->kind]);
/* i2c kind goes from 1-5, we want from 0-4 to address arrays */ err = device_create_file(&client->dev, &dev_attr_alert_led);
data->kind = kind - 1; if (err)
goto exit_detach;
for (i = 0; i < ARRAY_SIZE(fschmd_attr); i++) { for (i = 0; i < FSCHMD_NO_VOLT_SENSORS[data->kind]; i++) {
err = device_create_file(&client->dev, err = device_create_file(&client->dev,
&fschmd_attr[i].dev_attr); &fschmd_attr[i].dev_attr);
if (err) if (err)
...@@ -1027,6 +1117,16 @@ static int fschmd_probe(struct i2c_client *client, ...@@ -1027,6 +1117,16 @@ static int fschmd_probe(struct i2c_client *client,
show_temp_max) show_temp_max)
continue; continue;
if (kind == fscsyl) {
if (i % 4 == 0)
data->temp_status[i / 4] =
i2c_smbus_read_byte_data(client,
FSCHMD_REG_TEMP_STATE
[data->kind][i / 4]);
if (data->temp_status[i / 4] & FSCHMD_TEMP_DISABLED)
continue;
}
err = device_create_file(&client->dev, err = device_create_file(&client->dev,
&fschmd_temp_attr[i].dev_attr); &fschmd_temp_attr[i].dev_attr);
if (err) if (err)
...@@ -1040,6 +1140,16 @@ static int fschmd_probe(struct i2c_client *client, ...@@ -1040,6 +1140,16 @@ static int fschmd_probe(struct i2c_client *client,
"pwm3_auto_point1_pwm")) "pwm3_auto_point1_pwm"))
continue; continue;
if (kind == fscsyl) {
if (i % 5 == 0)
data->fan_status[i / 5] =
i2c_smbus_read_byte_data(client,
FSCHMD_REG_FAN_STATE
[data->kind][i / 5]);
if (data->fan_status[i / 5] & FSCHMD_FAN_DISABLED)
continue;
}
err = device_create_file(&client->dev, err = device_create_file(&client->dev,
&fschmd_fan_attr[i].dev_attr); &fschmd_fan_attr[i].dev_attr);
if (err) if (err)
...@@ -1126,7 +1236,8 @@ static int fschmd_remove(struct i2c_client *client) ...@@ -1126,7 +1236,8 @@ static int fschmd_remove(struct i2c_client *client)
if (data->hwmon_dev) if (data->hwmon_dev)
hwmon_device_unregister(data->hwmon_dev); hwmon_device_unregister(data->hwmon_dev);
for (i = 0; i < ARRAY_SIZE(fschmd_attr); i++) device_remove_file(&client->dev, &dev_attr_alert_led);
for (i = 0; i < (FSCHMD_NO_VOLT_SENSORS[data->kind]); i++)
device_remove_file(&client->dev, &fschmd_attr[i].dev_attr); device_remove_file(&client->dev, &fschmd_attr[i].dev_attr);
for (i = 0; i < (FSCHMD_NO_TEMP_SENSORS[data->kind] * 4); i++) for (i = 0; i < (FSCHMD_NO_TEMP_SENSORS[data->kind] * 4); i++)
device_remove_file(&client->dev, device_remove_file(&client->dev,
...@@ -1171,7 +1282,7 @@ static struct fschmd_data *fschmd_update_device(struct device *dev) ...@@ -1171,7 +1282,7 @@ static struct fschmd_data *fschmd_update_device(struct device *dev)
data->temp_act[i] < data->temp_max[i]) data->temp_act[i] < data->temp_max[i])
i2c_smbus_write_byte_data(client, i2c_smbus_write_byte_data(client,
FSCHMD_REG_TEMP_STATE[data->kind][i], FSCHMD_REG_TEMP_STATE[data->kind][i],
FSCHMD_TEMP_ALERT); data->temp_status[i]);
} }
for (i = 0; i < FSCHMD_NO_FAN_SENSORS[data->kind]; i++) { for (i = 0; i < FSCHMD_NO_FAN_SENSORS[data->kind]; i++) {
...@@ -1193,12 +1304,12 @@ static struct fschmd_data *fschmd_update_device(struct device *dev) ...@@ -1193,12 +1304,12 @@ static struct fschmd_data *fschmd_update_device(struct device *dev)
data->fan_act[i]) data->fan_act[i])
i2c_smbus_write_byte_data(client, i2c_smbus_write_byte_data(client,
FSCHMD_REG_FAN_STATE[data->kind][i], FSCHMD_REG_FAN_STATE[data->kind][i],
FSCHMD_FAN_ALARM); data->fan_status[i]);
} }
for (i = 0; i < 3; i++) for (i = 0; i < FSCHMD_NO_VOLT_SENSORS[data->kind]; i++)
data->volt[i] = i2c_smbus_read_byte_data(client, data->volt[i] = i2c_smbus_read_byte_data(client,
FSCHMD_REG_VOLT[i]); FSCHMD_REG_VOLT[data->kind][i]);
data->last_updated = jiffies; data->last_updated = jiffies;
data->valid = 1; data->valid = 1;
...@@ -1220,8 +1331,8 @@ static void __exit fschmd_exit(void) ...@@ -1220,8 +1331,8 @@ static void __exit fschmd_exit(void)
} }
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("FSC Poseidon, Hermes, Scylla, Heracles and " MODULE_DESCRIPTION("FSC Poseidon, Hermes, Scylla, Heracles, Heimdall, Hades "
"Heimdall driver"); "and Syleus driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
module_init(fschmd_init); module_init(fschmd_init);
......
...@@ -65,6 +65,10 @@ ...@@ -65,6 +65,10 @@
#define HDAPS_INPUT_FUZZ 4 /* input event threshold */ #define HDAPS_INPUT_FUZZ 4 /* input event threshold */
#define HDAPS_INPUT_FLAT 4 #define HDAPS_INPUT_FLAT 4
#define HDAPS_X_AXIS (1 << 0)
#define HDAPS_Y_AXIS (1 << 1)
#define HDAPS_BOTH_AXES (HDAPS_X_AXIS | HDAPS_Y_AXIS)
static struct platform_device *pdev; static struct platform_device *pdev;
static struct input_polled_dev *hdaps_idev; static struct input_polled_dev *hdaps_idev;
static unsigned int hdaps_invert; static unsigned int hdaps_invert;
...@@ -182,11 +186,11 @@ static int __hdaps_read_pair(unsigned int port1, unsigned int port2, ...@@ -182,11 +186,11 @@ static int __hdaps_read_pair(unsigned int port1, unsigned int port2,
km_activity = inb(HDAPS_PORT_KMACT); km_activity = inb(HDAPS_PORT_KMACT);
__device_complete(); __device_complete();
/* if hdaps_invert is set, negate the two values */ /* hdaps_invert is a bitvector to negate the axes */
if (hdaps_invert) { if (hdaps_invert & HDAPS_X_AXIS)
*x = -*x; *x = -*x;
if (hdaps_invert & HDAPS_Y_AXIS)
*y = -*y; *y = -*y;
}
return 0; return 0;
} }
...@@ -436,7 +440,8 @@ static ssize_t hdaps_invert_store(struct device *dev, ...@@ -436,7 +440,8 @@ static ssize_t hdaps_invert_store(struct device *dev,
{ {
int invert; int invert;
if (sscanf(buf, "%d", &invert) != 1 || (invert != 1 && invert != 0)) if (sscanf(buf, "%d", &invert) != 1 ||
invert < 0 || invert > HDAPS_BOTH_AXES)
return -EINVAL; return -EINVAL;
hdaps_invert = invert; hdaps_invert = invert;
...@@ -483,56 +488,52 @@ static int __init hdaps_dmi_match(const struct dmi_system_id *id) ...@@ -483,56 +488,52 @@ static int __init hdaps_dmi_match(const struct dmi_system_id *id)
/* hdaps_dmi_match_invert - found an inverted match. */ /* hdaps_dmi_match_invert - found an inverted match. */
static int __init hdaps_dmi_match_invert(const struct dmi_system_id *id) static int __init hdaps_dmi_match_invert(const struct dmi_system_id *id)
{ {
hdaps_invert = 1; hdaps_invert = (unsigned long)id->driver_data;
printk(KERN_INFO "hdaps: inverting axis readings.\n"); printk(KERN_INFO "hdaps: inverting axis (%u) readings.\n",
hdaps_invert);
return hdaps_dmi_match(id); return hdaps_dmi_match(id);
} }
#define HDAPS_DMI_MATCH_NORMAL(vendor, model) { \ #define HDAPS_DMI_MATCH_INVERT(vendor, model, axes) { \
.ident = vendor " " model, \
.callback = hdaps_dmi_match, \
.matches = { \
DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
DMI_MATCH(DMI_PRODUCT_VERSION, model) \
} \
}
#define HDAPS_DMI_MATCH_INVERT(vendor, model) { \
.ident = vendor " " model, \ .ident = vendor " " model, \
.callback = hdaps_dmi_match_invert, \ .callback = hdaps_dmi_match_invert, \
.driver_data = (void *)axes, \
.matches = { \ .matches = { \
DMI_MATCH(DMI_BOARD_VENDOR, vendor), \ DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
DMI_MATCH(DMI_PRODUCT_VERSION, model) \ DMI_MATCH(DMI_PRODUCT_VERSION, model) \
} \ } \
} }
#define HDAPS_DMI_MATCH_NORMAL(vendor, model) \
HDAPS_DMI_MATCH_INVERT(vendor, model, 0)
/* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match /* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match
"ThinkPad T42p", so the order of the entries matters. "ThinkPad T42p", so the order of the entries matters.
If your ThinkPad is not recognized, please update to latest If your ThinkPad is not recognized, please update to latest
BIOS. This is especially the case for some R52 ThinkPads. */ BIOS. This is especially the case for some R52 ThinkPads. */
static struct dmi_system_id __initdata hdaps_whitelist[] = { static struct dmi_system_id __initdata hdaps_whitelist[] = {
HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p"), HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R50"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R50"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61i"), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61i", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61"), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p"), HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T41"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T41"),
HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p"), HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T42"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T42"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T43"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T43"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60"), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p"), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61"), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X41"), HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X41", HDAPS_Y_AXIS),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60"), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s"), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61"), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"), HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61m"), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61m", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61p"), HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61p", HDAPS_BOTH_AXES),
{ .ident = NULL } { .ident = NULL }
}; };
...@@ -627,8 +628,9 @@ static void __exit hdaps_exit(void) ...@@ -627,8 +628,9 @@ static void __exit hdaps_exit(void)
module_init(hdaps_init); module_init(hdaps_init);
module_exit(hdaps_exit); module_exit(hdaps_exit);
module_param_named(invert, hdaps_invert, bool, 0); module_param_named(invert, hdaps_invert, int, 0);
MODULE_PARM_DESC(invert, "invert data along each axis"); MODULE_PARM_DESC(invert, "invert data along each axis. 1 invert x-axis, "
"2 invert y-axis, 3 invert both axes.");
MODULE_AUTHOR("Robert Love"); MODULE_AUTHOR("Robert Love");
MODULE_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver"); MODULE_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver");
......
/* /*
Copyright (C) 2001-2004 Aurelien Jarno <aurelien@aurel32.net> Copyright (C) 2001-2004 Aurelien Jarno <aurelien@aurel32.net>
Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with
the help of Jean Delvare <khali@linux-fr.org> the help of 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
...@@ -41,13 +41,13 @@ MODULE_PARM_DESC(input_mode, ...@@ -41,13 +41,13 @@ MODULE_PARM_DESC(input_mode,
" 3 = two differential inputs\n"); " 3 = two differential inputs\n");
/* The PCF8591 control byte /* The PCF8591 control byte
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
| 0 |AOEF| AIP | 0 |AINC| AICH | */ | 0 |AOEF| AIP | 0 |AINC| AICH | */
/* Analog Output Enable Flag (analog output active if 1) */ /* Analog Output Enable Flag (analog output active if 1) */
#define PCF8591_CONTROL_AOEF 0x40 #define PCF8591_CONTROL_AOEF 0x40
/* Analog Input Programming /* Analog Input Programming
0x00 = four single ended inputs 0x00 = four single ended inputs
0x10 = three differential inputs 0x10 = three differential inputs
0x20 = single ended and differential mixed 0x20 = single ended and differential mixed
...@@ -58,7 +58,7 @@ MODULE_PARM_DESC(input_mode, ...@@ -58,7 +58,7 @@ MODULE_PARM_DESC(input_mode,
#define PCF8591_CONTROL_AINC 0x04 #define PCF8591_CONTROL_AINC 0x04
/* Channel selection /* Channel selection
0x00 = channel 0 0x00 = channel 0
0x01 = channel 1 0x01 = channel 1
0x02 = channel 2 0x02 = channel 2
0x03 = channel 3 */ 0x03 = channel 3 */
...@@ -114,7 +114,7 @@ static ssize_t set_out0_output(struct device *dev, struct device_attribute *attr ...@@ -114,7 +114,7 @@ static ssize_t set_out0_output(struct device *dev, struct device_attribute *attr
return -EINVAL; return -EINVAL;
} }
static DEVICE_ATTR(out0_output, S_IWUSR | S_IRUGO, static DEVICE_ATTR(out0_output, S_IWUSR | S_IRUGO,
show_out0_ouput, set_out0_output); show_out0_ouput, set_out0_output);
static ssize_t show_out0_enable(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t show_out0_enable(struct device *dev, struct device_attribute *attr, char *buf)
...@@ -139,7 +139,7 @@ static ssize_t set_out0_enable(struct device *dev, struct device_attribute *attr ...@@ -139,7 +139,7 @@ static ssize_t set_out0_enable(struct device *dev, struct device_attribute *attr
return count; return count;
} }
static DEVICE_ATTR(out0_enable, S_IWUSR | S_IRUGO, static DEVICE_ATTR(out0_enable, S_IWUSR | S_IRUGO,
show_out0_enable, set_out0_enable); show_out0_enable, set_out0_enable);
static struct attribute *pcf8591_attributes[] = { static struct attribute *pcf8591_attributes[] = {
...@@ -196,7 +196,7 @@ static int pcf8591_probe(struct i2c_client *client, ...@@ -196,7 +196,7 @@ static int pcf8591_probe(struct i2c_client *client,
err = -ENOMEM; err = -ENOMEM;
goto exit; goto exit;
} }
i2c_set_clientdata(client, data); i2c_set_clientdata(client, data);
mutex_init(&data->update_lock); mutex_init(&data->update_lock);
...@@ -249,8 +249,8 @@ static void pcf8591_init_client(struct i2c_client *client) ...@@ -249,8 +249,8 @@ static void pcf8591_init_client(struct i2c_client *client)
data->aout = PCF8591_INIT_AOUT; data->aout = PCF8591_INIT_AOUT;
i2c_smbus_write_byte_data(client, data->control, data->aout); i2c_smbus_write_byte_data(client, data->control, data->aout);
/* The first byte transmitted contains the conversion code of the /* The first byte transmitted contains the conversion code of the
previous read cycle. FLUSH IT! */ previous read cycle. FLUSH IT! */
i2c_smbus_read_byte(client); i2c_smbus_read_byte(client);
} }
...@@ -267,8 +267,8 @@ static int pcf8591_read_channel(struct device *dev, int channel) ...@@ -267,8 +267,8 @@ static int pcf8591_read_channel(struct device *dev, int channel)
data->control = (data->control & ~PCF8591_CONTROL_AICH_MASK) data->control = (data->control & ~PCF8591_CONTROL_AICH_MASK)
| channel; | channel;
i2c_smbus_write_byte(client, data->control); i2c_smbus_write_byte(client, data->control);
/* The first byte transmitted contains the conversion code of /* The first byte transmitted contains the conversion code of
the previous read cycle. FLUSH IT! */ the previous read cycle. FLUSH IT! */
i2c_smbus_read_byte(client); i2c_smbus_read_byte(client);
} }
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
w83627ehf 10 5 4 3 0x8850 0x88 0x5ca3 w83627ehf 10 5 4 3 0x8850 0x88 0x5ca3
0x8860 0xa1 0x8860 0xa1
w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3 w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3
w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -52,12 +53,13 @@ ...@@ -52,12 +53,13 @@
#include <asm/io.h> #include <asm/io.h>
#include "lm75.h" #include "lm75.h"
enum kinds { w83627ehf, w83627dhg }; enum kinds { w83627ehf, w83627dhg, w83667hg };
/* 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[] = {
"w83627ehf", "w83627ehf",
"w83627dhg", "w83627dhg",
"w83667hg",
}; };
static unsigned short force_id; static unsigned short force_id;
...@@ -71,6 +73,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID"); ...@@ -71,6 +73,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID");
*/ */
#define W83627EHF_LD_HWM 0x0b #define W83627EHF_LD_HWM 0x0b
#define W83667HG_LD_VID 0x0d
#define SIO_REG_LDSEL 0x07 /* Logical device select */ #define SIO_REG_LDSEL 0x07 /* Logical device select */
#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ #define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
...@@ -83,6 +86,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID"); ...@@ -83,6 +86,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID");
#define SIO_W83627EHF_ID 0x8850 #define SIO_W83627EHF_ID 0x8850
#define SIO_W83627EHG_ID 0x8860 #define SIO_W83627EHG_ID 0x8860
#define SIO_W83627DHG_ID 0xa020 #define SIO_W83627DHG_ID 0xa020
#define SIO_W83667HG_ID 0xa510
#define SIO_ID_MASK 0xFFF0 #define SIO_ID_MASK 0xFFF0
static inline void static inline void
...@@ -289,6 +293,7 @@ struct w83627ehf_data { ...@@ -289,6 +293,7 @@ struct w83627ehf_data {
u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */ u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */
u8 pwm_enable[4]; /* 1->manual u8 pwm_enable[4]; /* 1->manual
2->thermal cruise (also called SmartFan I) */ 2->thermal cruise (also called SmartFan I) */
u8 pwm_num; /* number of pwm */
u8 pwm[4]; u8 pwm[4];
u8 target_temp[4]; u8 target_temp[4];
u8 tolerance[4]; u8 tolerance[4];
...@@ -298,6 +303,9 @@ struct w83627ehf_data { ...@@ -298,6 +303,9 @@ struct w83627ehf_data {
u8 vid; u8 vid;
u8 vrm; u8 vrm;
u8 temp3_disable;
u8 in6_skip;
}; };
struct w83627ehf_sio_data { struct w83627ehf_sio_data {
...@@ -866,25 +874,37 @@ show_temp_type(struct device *dev, struct device_attribute *attr, char *buf) ...@@ -866,25 +874,37 @@ show_temp_type(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%d\n", (int)data->temp_type[nr]); return sprintf(buf, "%d\n", (int)data->temp_type[nr]);
} }
static struct sensor_device_attribute sda_temp[] = { static struct sensor_device_attribute sda_temp_input[] = {
SENSOR_ATTR(temp1_input, S_IRUGO, show_temp1, NULL, 0), SENSOR_ATTR(temp1_input, S_IRUGO, show_temp1, NULL, 0),
SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 0), SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 0),
SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 1), SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 1),
};
static struct sensor_device_attribute sda_temp_max[] = {
SENSOR_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp1_max, SENSOR_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp1_max,
store_temp1_max, 0), store_temp1_max, 0),
SENSOR_ATTR(temp2_max, S_IRUGO | S_IWUSR, show_temp_max, SENSOR_ATTR(temp2_max, S_IRUGO | S_IWUSR, show_temp_max,
store_temp_max, 0), store_temp_max, 0),
SENSOR_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max, SENSOR_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max,
store_temp_max, 1), store_temp_max, 1),
};
static struct sensor_device_attribute sda_temp_max_hyst[] = {
SENSOR_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp1_max_hyst, SENSOR_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp1_max_hyst,
store_temp1_max_hyst, 0), store_temp1_max_hyst, 0),
SENSOR_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, SENSOR_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst,
store_temp_max_hyst, 0), store_temp_max_hyst, 0),
SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst,
store_temp_max_hyst, 1), store_temp_max_hyst, 1),
};
static struct sensor_device_attribute sda_temp_alarm[] = {
SENSOR_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4), SENSOR_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4),
SENSOR_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5), SENSOR_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5),
SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13), SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13),
};
static struct sensor_device_attribute sda_temp_type[] = {
SENSOR_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0), SENSOR_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0),
SENSOR_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1), SENSOR_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1),
SENSOR_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2), SENSOR_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2),
...@@ -1181,6 +1201,8 @@ static void w83627ehf_device_remove_files(struct device *dev) ...@@ -1181,6 +1201,8 @@ static void w83627ehf_device_remove_files(struct device *dev)
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++) {
if ((i == 6) && data->in6_skip)
continue;
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);
...@@ -1192,15 +1214,22 @@ static void w83627ehf_device_remove_files(struct device *dev) ...@@ -1192,15 +1214,22 @@ static void w83627ehf_device_remove_files(struct device *dev)
device_remove_file(dev, &sda_fan_div[i].dev_attr); device_remove_file(dev, &sda_fan_div[i].dev_attr);
device_remove_file(dev, &sda_fan_min[i].dev_attr); device_remove_file(dev, &sda_fan_min[i].dev_attr);
} }
for (i = 0; i < 4; i++) { for (i = 0; i < data->pwm_num; i++) {
device_remove_file(dev, &sda_pwm[i].dev_attr); device_remove_file(dev, &sda_pwm[i].dev_attr);
device_remove_file(dev, &sda_pwm_mode[i].dev_attr); device_remove_file(dev, &sda_pwm_mode[i].dev_attr);
device_remove_file(dev, &sda_pwm_enable[i].dev_attr); device_remove_file(dev, &sda_pwm_enable[i].dev_attr);
device_remove_file(dev, &sda_target_temp[i].dev_attr); device_remove_file(dev, &sda_target_temp[i].dev_attr);
device_remove_file(dev, &sda_tolerance[i].dev_attr); device_remove_file(dev, &sda_tolerance[i].dev_attr);
} }
for (i = 0; i < ARRAY_SIZE(sda_temp); i++) for (i = 0; i < 3; i++) {
device_remove_file(dev, &sda_temp[i].dev_attr); if ((i == 2) && data->temp3_disable)
continue;
device_remove_file(dev, &sda_temp_input[i].dev_attr);
device_remove_file(dev, &sda_temp_max[i].dev_attr);
device_remove_file(dev, &sda_temp_max_hyst[i].dev_attr);
device_remove_file(dev, &sda_temp_alarm[i].dev_attr);
device_remove_file(dev, &sda_temp_type[i].dev_attr);
}
device_remove_file(dev, &dev_attr_name); device_remove_file(dev, &dev_attr_name);
device_remove_file(dev, &dev_attr_cpu0_vid); device_remove_file(dev, &dev_attr_cpu0_vid);
...@@ -1222,6 +1251,8 @@ static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data) ...@@ -1222,6 +1251,8 @@ static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data)
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
tmp = w83627ehf_read_value(data, tmp = w83627ehf_read_value(data,
W83627EHF_REG_TEMP_CONFIG[i]); W83627EHF_REG_TEMP_CONFIG[i]);
if ((i == 1) && data->temp3_disable)
continue;
if (tmp & 0x01) if (tmp & 0x01)
w83627ehf_write_value(data, w83627ehf_write_value(data,
W83627EHF_REG_TEMP_CONFIG[i], W83627EHF_REG_TEMP_CONFIG[i],
...@@ -1272,8 +1303,17 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) ...@@ -1272,8 +1303,17 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
data->name = w83627ehf_device_names[sio_data->kind]; data->name = w83627ehf_device_names[sio_data->kind];
platform_set_drvdata(pdev, data); platform_set_drvdata(pdev, data);
/* 627EHG and 627EHF have 10 voltage inputs; DHG has 9 */ /* 627EHG and 627EHF have 10 voltage inputs; 627DHG and 667HG have 9 */
data->in_num = (sio_data->kind == w83627dhg) ? 9 : 10; data->in_num = (sio_data->kind == w83627ehf) ? 10 : 9;
/* 667HG has 3 pwms */
data->pwm_num = (sio_data->kind == w83667hg) ? 3 : 4;
/* Check temp3 configuration bit for 667HG */
if (sio_data->kind == w83667hg) {
data->temp3_disable = w83627ehf_read_value(data,
W83627EHF_REG_TEMP_CONFIG[1]) & 0x01;
data->in6_skip = !data->temp3_disable;
}
/* Initialize the chip */ /* Initialize the chip */
w83627ehf_init_device(data); w83627ehf_init_device(data);
...@@ -1281,44 +1321,64 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) ...@@ -1281,44 +1321,64 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
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 */
superio_select(sio_data->sioreg, W83627EHF_LD_HWM); if (sio_data->kind == w83667hg) {
if (superio_inb(sio_data->sioreg, SIO_REG_VID_CTRL) & 0x80) { /* W83667HG has different pins for VID input and output, so
/* Set VID input sensibility if needed. In theory the BIOS we can get the VID input values directly at logical device D
should have set it, but in practice it's not always the 0xe3. */
case. We only do it for the W83627EHF/EHG because the superio_select(sio_data->sioreg, W83667HG_LD_VID);
W83627DHG is more complex in this respect. */ data->vid = superio_inb(sio_data->sioreg, 0xe3);
if (sio_data->kind == w83627ehf) {
en_vrm10 = superio_inb(sio_data->sioreg,
SIO_REG_EN_VRM10);
if ((en_vrm10 & 0x08) && data->vrm == 90) {
dev_warn(dev, "Setting VID input voltage to "
"TTL\n");
superio_outb(sio_data->sioreg, SIO_REG_EN_VRM10,
en_vrm10 & ~0x08);
} else if (!(en_vrm10 & 0x08) && data->vrm == 100) {
dev_warn(dev, "Setting VID input voltage to "
"VRM10\n");
superio_outb(sio_data->sioreg, SIO_REG_EN_VRM10,
en_vrm10 | 0x08);
}
}
data->vid = superio_inb(sio_data->sioreg, SIO_REG_VID_DATA);
if (sio_data->kind == w83627ehf) /* 6 VID pins only */
data->vid &= 0x3f;
err = device_create_file(dev, &dev_attr_cpu0_vid); err = device_create_file(dev, &dev_attr_cpu0_vid);
if (err) if (err)
goto exit_release; goto exit_release;
} else { } else {
dev_info(dev, "VID pins in output mode, CPU VID not " superio_select(sio_data->sioreg, W83627EHF_LD_HWM);
"available\n"); if (superio_inb(sio_data->sioreg, SIO_REG_VID_CTRL) & 0x80) {
/* Set VID input sensibility if needed. In theory the
BIOS should have set it, but in practice it's not
always the case. We only do it for the W83627EHF/EHG
because the W83627DHG is more complex in this
respect. */
if (sio_data->kind == w83627ehf) {
en_vrm10 = superio_inb(sio_data->sioreg,
SIO_REG_EN_VRM10);
if ((en_vrm10 & 0x08) && data->vrm == 90) {
dev_warn(dev, "Setting VID input "
"voltage to TTL\n");
superio_outb(sio_data->sioreg,
SIO_REG_EN_VRM10,
en_vrm10 & ~0x08);
} else if (!(en_vrm10 & 0x08)
&& data->vrm == 100) {
dev_warn(dev, "Setting VID input "
"voltage to VRM10\n");
superio_outb(sio_data->sioreg,
SIO_REG_EN_VRM10,
en_vrm10 | 0x08);
}
}
data->vid = superio_inb(sio_data->sioreg,
SIO_REG_VID_DATA);
if (sio_data->kind == w83627ehf) /* 6 VID pins only */
data->vid &= 0x3f;
err = device_create_file(dev, &dev_attr_cpu0_vid);
if (err)
goto exit_release;
} else {
dev_info(dev, "VID pins in output mode, CPU VID not "
"available\n");
}
} }
/* 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) {
fan5pin = superio_inb(sio_data->sioreg, 0x24) & 0x2; fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20;
fan4pin = superio_inb(sio_data->sioreg, 0x29) & 0x6; fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40;
} else {
fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x02);
fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x06);
}
superio_exit(sio_data->sioreg); superio_exit(sio_data->sioreg);
/* It looks like fan4 and fan5 pins can be alternatively used /* It looks like fan4 and fan5 pins can be alternatively used
...@@ -1329,9 +1389,9 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) ...@@ -1329,9 +1389,9 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
data->has_fan = 0x07; /* fan1, fan2 and fan3 */ data->has_fan = 0x07; /* fan1, fan2 and fan3 */
i = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1); i = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1);
if ((i & (1 << 2)) && (!fan4pin)) if ((i & (1 << 2)) && fan4pin)
data->has_fan |= (1 << 3); data->has_fan |= (1 << 3);
if (!(i & (1 << 1)) && (!fan5pin)) if (!(i & (1 << 1)) && fan5pin)
data->has_fan |= (1 << 4); data->has_fan |= (1 << 4);
/* Read fan clock dividers immediately */ /* Read fan clock dividers immediately */
...@@ -1344,14 +1404,16 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) ...@@ -1344,14 +1404,16 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
goto exit_remove; 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)) 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++) {
if ((err = device_create_file(dev, if ((err = device_create_file(dev,
&sda_sf3_arrays_fan4[i].dev_attr))) &sda_sf3_arrays_fan4[i].dev_attr)))
goto exit_remove; goto exit_remove;
} }
for (i = 0; i < data->in_num; i++) for (i = 0; i < data->in_num; i++) {
if ((i == 6) && data->in6_skip)
continue;
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))
...@@ -1360,6 +1422,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) ...@@ -1360,6 +1422,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
|| (err = device_create_file(dev, || (err = device_create_file(dev,
&sda_in_max[i].dev_attr))) &sda_in_max[i].dev_attr)))
goto exit_remove; goto exit_remove;
}
for (i = 0; i < 5; i++) { for (i = 0; i < 5; i++) {
if (data->has_fan & (1 << i)) { if (data->has_fan & (1 << i)) {
...@@ -1372,7 +1435,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) ...@@ -1372,7 +1435,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
|| (err = device_create_file(dev, || (err = device_create_file(dev,
&sda_fan_min[i].dev_attr))) &sda_fan_min[i].dev_attr)))
goto exit_remove; goto exit_remove;
if (i < 4 && /* w83627ehf only has 4 pwm */ if (i < data->pwm_num &&
((err = device_create_file(dev, ((err = device_create_file(dev,
&sda_pwm[i].dev_attr)) &sda_pwm[i].dev_attr))
|| (err = device_create_file(dev, || (err = device_create_file(dev,
...@@ -1387,9 +1450,21 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) ...@@ -1387,9 +1450,21 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
} }
} }
for (i = 0; i < ARRAY_SIZE(sda_temp); i++) for (i = 0; i < 3; i++) {
if ((err = device_create_file(dev, &sda_temp[i].dev_attr))) if ((i == 2) && data->temp3_disable)
continue;
if ((err = device_create_file(dev,
&sda_temp_input[i].dev_attr))
|| (err = device_create_file(dev,
&sda_temp_max[i].dev_attr))
|| (err = device_create_file(dev,
&sda_temp_max_hyst[i].dev_attr))
|| (err = device_create_file(dev,
&sda_temp_alarm[i].dev_attr))
|| (err = device_create_file(dev,
&sda_temp_type[i].dev_attr)))
goto exit_remove; goto exit_remove;
}
err = device_create_file(dev, &dev_attr_name); err = device_create_file(dev, &dev_attr_name);
if (err) if (err)
...@@ -1442,6 +1517,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, ...@@ -1442,6 +1517,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
static const char __initdata sio_name_W83627EHF[] = "W83627EHF"; static const char __initdata sio_name_W83627EHF[] = "W83627EHF";
static const char __initdata sio_name_W83627EHG[] = "W83627EHG"; static const char __initdata sio_name_W83627EHG[] = "W83627EHG";
static const char __initdata sio_name_W83627DHG[] = "W83627DHG"; static const char __initdata sio_name_W83627DHG[] = "W83627DHG";
static const char __initdata sio_name_W83667HG[] = "W83667HG";
u16 val; u16 val;
const char *sio_name; const char *sio_name;
...@@ -1466,6 +1542,10 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, ...@@ -1466,6 +1542,10 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
sio_data->kind = w83627dhg; sio_data->kind = w83627dhg;
sio_name = sio_name_W83627DHG; sio_name = sio_name_W83627DHG;
break; break;
case SIO_W83667HG_ID:
sio_data->kind = w83667hg;
sio_name = sio_name_W83667HG;
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",
......
...@@ -65,6 +65,7 @@ ...@@ -65,6 +65,7 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/dmi.h>
/* I801 SMBus address offsets */ /* I801 SMBus address offsets */
#define SMBHSTSTS (0 + i801_smba) #define SMBHSTSTS (0 + i801_smba)
...@@ -616,10 +617,81 @@ static void __init input_apanel_init(void) ...@@ -616,10 +617,81 @@ static void __init input_apanel_init(void)
static void __init input_apanel_init(void) {} static void __init input_apanel_init(void) {}
#endif #endif
#if defined CONFIG_SENSORS_FSCHMD || defined CONFIG_SENSORS_FSCHMD_MODULE
struct dmi_onboard_device_info {
const char *name;
u8 type;
unsigned short i2c_addr;
const char *i2c_type;
};
static struct dmi_onboard_device_info __devinitdata dmi_devices[] = {
{ "Syleus", DMI_DEV_TYPE_OTHER, 0x73, "fscsyl" },
{ "Hermes", DMI_DEV_TYPE_OTHER, 0x73, "fscher" },
{ "Hades", DMI_DEV_TYPE_OTHER, 0x73, "fschds" },
};
static void __devinit dmi_check_onboard_device(u8 type, const char *name,
struct i2c_adapter *adap)
{
int i;
struct i2c_board_info info;
for (i = 0; i < ARRAY_SIZE(dmi_devices); i++) {
/* & ~0x80, ignore enabled/disabled bit */
if ((type & ~0x80) != dmi_devices[i].type)
continue;
if (strcmp(name, dmi_devices[i].name))
continue;
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = dmi_devices[i].i2c_addr;
strlcpy(info.type, dmi_devices[i].i2c_type, I2C_NAME_SIZE);
i2c_new_device(adap, &info);
break;
}
}
/* We use our own function to check for onboard devices instead of
dmi_find_device() as some buggy BIOS's have the devices we are interested
in marked as disabled */
static void __devinit dmi_check_onboard_devices(const struct dmi_header *dm,
void *adap)
{
int i, count;
if (dm->type != 10)
return;
count = (dm->length - sizeof(struct dmi_header)) / 2;
for (i = 0; i < count; i++) {
const u8 *d = (char *)(dm + 1) + (i * 2);
const char *name = ((char *) dm) + dm->length;
u8 type = d[0];
u8 s = d[1];
if (!s)
continue;
s--;
while (s > 0 && name[0]) {
name += strlen(name) + 1;
s--;
}
if (name[0] == 0) /* Bogus string reference */
continue;
dmi_check_onboard_device(type, name, adap);
}
}
#endif
static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id) static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
{ {
unsigned char temp; unsigned char temp;
int err; int err;
#if defined CONFIG_SENSORS_FSCHMD || defined CONFIG_SENSORS_FSCHMD_MODULE
const char *vendor;
#endif
I801_dev = dev; I801_dev = dev;
i801_features = 0; i801_features = 0;
...@@ -712,6 +784,11 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id ...@@ -712,6 +784,11 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id
i2c_new_device(&i801_adapter, &info); i2c_new_device(&i801_adapter, &info);
} }
#endif #endif
#if defined CONFIG_SENSORS_FSCHMD || defined CONFIG_SENSORS_FSCHMD_MODULE
vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
if (vendor && !strcmp(vendor, "FUJITSU SIEMENS"))
dmi_walk(dmi_check_onboard_devices, &i801_adapter);
#endif
return 0; return 0;
......
...@@ -64,19 +64,6 @@ config SENSORS_PCA9539 ...@@ -64,19 +64,6 @@ config SENSORS_PCA9539
This driver is deprecated and will be dropped soon. Use This driver is deprecated and will be dropped soon. Use
drivers/gpio/pca953x.c instead. drivers/gpio/pca953x.c instead.
config SENSORS_PCF8591
tristate "Philips PCF8591"
depends on EXPERIMENTAL
default n
help
If you say yes here you get support for Philips PCF8591 chips.
This driver can also be built as a module. If so, the module
will be called pcf8591.
These devices are hard to detect and rarely found on mainstream
hardware. If unsure, say N.
config SENSORS_MAX6875 config SENSORS_MAX6875
tristate "Maxim MAX6875 Power supply supervisor" tristate "Maxim MAX6875 Power supply supervisor"
depends on EXPERIMENTAL depends on EXPERIMENTAL
......
...@@ -15,7 +15,6 @@ obj-$(CONFIG_SENSORS_MAX6875) += max6875.o ...@@ -15,7 +15,6 @@ obj-$(CONFIG_SENSORS_MAX6875) += max6875.o
obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o
obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
obj-$(CONFIG_PCF8575) += pcf8575.o obj-$(CONFIG_PCF8575) += pcf8575.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
......
...@@ -103,7 +103,7 @@ static void parse_da_table(const struct dmi_header *dm) ...@@ -103,7 +103,7 @@ static void parse_da_table(const struct dmi_header *dm)
da_num_tokens += tokens; da_num_tokens += tokens;
} }
static void find_tokens(const struct dmi_header *dm) static void find_tokens(const struct dmi_header *dm, void *dummy)
{ {
switch (dm->type) { switch (dm->type) {
case 0xd4: /* Indexed IO */ case 0xd4: /* Indexed IO */
...@@ -356,7 +356,7 @@ static int __init dell_init(void) ...@@ -356,7 +356,7 @@ static int __init dell_init(void)
if (!dmi_check_system(dell_device_table)) if (!dmi_check_system(dell_device_table))
return -ENODEV; return -ENODEV;
dmi_walk(find_tokens); dmi_walk(find_tokens, NULL);
if (!da_tokens) { if (!da_tokens) {
printk(KERN_INFO "dell-laptop: Unable to find dmi tokens\n"); printk(KERN_INFO "dell-laptop: Unable to find dmi tokens\n");
......
...@@ -380,7 +380,7 @@ asm(".text \n\t" ...@@ -380,7 +380,7 @@ asm(".text \n\t"
* This function checks whether or not a SMBIOS/DMI record is * This function checks whether or not a SMBIOS/DMI record is
* the 64bit CRU info or not * the 64bit CRU info or not
*/ */
static void __devinit dmi_find_cru(const struct dmi_header *dm) static void __devinit dmi_find_cru(const struct dmi_header *dm, void *dummy)
{ {
struct smbios_cru64_info *smbios_cru64_ptr; struct smbios_cru64_info *smbios_cru64_ptr;
unsigned long cru_physical_address; unsigned long cru_physical_address;
...@@ -403,7 +403,7 @@ static int __devinit detect_cru_service(void) ...@@ -403,7 +403,7 @@ static int __devinit detect_cru_service(void)
{ {
cru_rom_addr = NULL; cru_rom_addr = NULL;
dmi_walk(dmi_find_cru); dmi_walk(dmi_find_cru, NULL);
/* if cru_rom_addr has been set then we found a CRU service */ /* if cru_rom_addr has been set then we found a CRU service */
return ((cru_rom_addr != NULL) ? 0 : -ENODEV); return ((cru_rom_addr != NULL) ? 0 : -ENODEV);
......
...@@ -47,7 +47,8 @@ extern int dmi_get_year(int field); ...@@ -47,7 +47,8 @@ extern int dmi_get_year(int field);
extern int dmi_name_in_vendors(const char *str); extern int dmi_name_in_vendors(const char *str);
extern int dmi_name_in_serial(const char *str); extern int dmi_name_in_serial(const char *str);
extern int dmi_available; extern int dmi_available;
extern int dmi_walk(void (*decode)(const struct dmi_header *)); extern int dmi_walk(void (*decode)(const struct dmi_header *, void *),
void *private_data);
extern bool dmi_match(enum dmi_field f, const char *str); extern bool dmi_match(enum dmi_field f, const char *str);
#else #else
...@@ -61,8 +62,8 @@ static inline int dmi_get_year(int year) { return 0; } ...@@ -61,8 +62,8 @@ static inline int dmi_get_year(int year) { return 0; }
static inline int dmi_name_in_vendors(const char *s) { return 0; } static inline int dmi_name_in_vendors(const char *s) { return 0; }
static inline int dmi_name_in_serial(const char *s) { return 0; } static inline int dmi_name_in_serial(const char *s) { return 0; }
#define dmi_available 0 #define dmi_available 0
static inline int dmi_walk(void (*decode)(const struct dmi_header *)) static inline int dmi_walk(void (*decode)(const struct dmi_header *, void *),
{ return -1; } void *private_data) { return -1; }
static inline bool dmi_match(enum dmi_field f, const char *str) static inline bool dmi_match(enum dmi_field f, const char *str)
{ return false; } { return false; }
static inline const struct dmi_system_id * static inline const struct dmi_system_id *
......
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