提交 3c6fae67 编写于 作者: L Linus Torvalds

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

* 'hwmon-for-linus' of git://jdelvare.pck.nerim.net/jdelvare-2.6:
  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
......@@ -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
1.0 degree Celsius to avoid Tout "bouncing", though!
As for alarms, you can read the alarm status of the DS1621 via the 'alarms'
/sys file interface. The result consists mainly of bit 6 and 5 of the
configuration register of the chip; bit 6 (0x40 or 64) is the high alarm
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 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
status of Tout. They will only tell you about its history, whether or not
......@@ -64,45 +61,3 @@ with neither of the alarms set.
Temperature conversion of the DS1621 takes up to 1000ms; internal access to
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
Unit: microJoule
RO
**********
* Alarms *
**********
......@@ -453,6 +454,27 @@ beep_mask Bitmask for beep.
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
-------------------------------------
......
......@@ -2,30 +2,40 @@ Kernel driver w83627ehf
=======================
Supported chips:
* Winbond W83627EHF/EHG/DHG (ISA access ONLY)
* Winbond W83627EHF/EHG (ISA access ONLY)
Prefix: 'w83627ehf'
Addresses scanned: ISA address retrieved from Super I/O registers
Datasheet:
http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83627EHF_%20W83627EHGb.pdf
DHG datasheet confidential.
http://www.nuvoton.com.tw/NR/rdonlyres/A6A258F0-F0C9-4F97-81C0-C4D29E7E943E/0/W83627EHF.pdf
* 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:
Jean Delvare <khali@linux-fr.org>
Yuan Mu (Winbond)
Rudolf Marek <r.marek@assembler.cz>
David Hubbard <david.c.hubbard@gmail.com>
Gong Jun <JGong@nuvoton.com>
Description
-----------
This driver implements support for the Winbond W83627EHF, W83627EHG, and
W83627DHG super I/O chips. We will refer to them collectively as Winbond chips.
This driver implements support for the Winbond W83627EHF, W83627EHG,
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
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
warnings (control unimplemented), and some automatic fan regulation
strategies (plus manual fan control mode).
VID (6 pins for the 627EHF/EHG, 8 pins for the 627DHG and 667HG), alarms
with beep warnings (control unimplemented), and some automatic fan
regulation strategies (plus manual fan control mode).
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
......@@ -54,7 +64,8 @@ follows:
temp1 -> pwm1
temp2 -> pwm2
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
----------
......
......@@ -68,7 +68,8 @@ static char * __init dmi_string(const struct dmi_header *dm, u8 s)
* pointing to completely the wrong place for example
*/
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;
int i = 0;
......@@ -89,7 +90,7 @@ static void dmi_table(u8 *buf, int len, int num,
while ((data - buf < len - 1) && (data[0] || data[1]))
data++;
if (data - buf < len - 1)
decode(dm);
decode(dm, private_data);
data += 2;
i++;
}
......@@ -99,7 +100,8 @@ static u32 dmi_base;
static u16 dmi_len;
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;
......@@ -107,7 +109,7 @@ static int __init dmi_walk_early(void (*decode)(const struct dmi_header *))
if (buf == NULL)
return -1;
dmi_table(buf, dmi_len, dmi_num, decode);
dmi_table(buf, dmi_len, dmi_num, decode, NULL);
dmi_iounmap(buf, dmi_len);
return 0;
......@@ -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
* 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) {
case 0: /* BIOS Information */
......@@ -598,10 +600,12 @@ int dmi_get_year(int field)
/**
* dmi_walk - Walk the DMI table and get called back for every record
* @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.
*/
int dmi_walk(void (*decode)(const struct dmi_header *))
int dmi_walk(void (*decode)(const struct dmi_header *, void *),
void *private_data)
{
u8 *buf;
......@@ -612,7 +616,7 @@ int dmi_walk(void (*decode)(const struct dmi_header *))
if (buf == NULL)
return -1;
dmi_table(buf, dmi_len, dmi_num, decode);
dmi_table(buf, dmi_len, dmi_num, decode, private_data);
iounmap(buf);
return 0;
......
......@@ -343,12 +343,13 @@ config SENSORS_FSCPOS
will be called fscpos.
config SENSORS_FSCHMD
tristate "FSC Poseidon, Scylla, Hermes, Heimdall and Heracles"
tristate "Fujitsu Siemens Computers sensor chips"
depends on X86 && I2C
help
If you say yes here you get support for various Fujitsu Siemens
Computers sensor chips, including support for the integrated
watchdog.
If you say yes here you get support for the following Fujitsu
Siemens Computers (FSC) sensor chips: Poseidon, Scylla, Hermes,
Heimdall, Heracles, Hades and Syleus including support for the
integrated watchdog.
This is a merged driver for FSC sensor chips replacing the fscpos,
fscscy and fscher drivers and adding support for several other FSC
......@@ -635,6 +636,20 @@ config SENSORS_PC87427
This driver can also be built as a module. If so, the module
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
tristate "Silicon Integrated Systems Corp. SiS5595"
depends on PCI
......@@ -827,7 +842,7 @@ config SENSORS_W83627HF
will be called w83627hf.
config SENSORS_W83627EHF
tristate "Winbond W83627EHF/DHG"
tristate "Winbond W83627EHF/EHG/DHG, W83667HG"
select HWMON_VID
help
If you say yes here you get support for the hardware
......@@ -838,6 +853,8 @@ config SENSORS_W83627EHF
chip suited for specific Intel processors that use PECI such as
the Core 2 Duo.
This driver also supports the W83667HG chip.
This driver can also be built as a module. If so, the module
will be called w83627ehf.
......
......@@ -70,6 +70,7 @@ obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o
obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
......
......@@ -81,71 +81,84 @@ struct ds1621_data {
u8 conf; /* Register encoding, combined */
};
static int ds1621_probe(struct i2c_client *client,
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.
/* Temperature registers are word-sized.
DS1621 uses a high-byte first convention, which is exactly opposite to
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)
return i2c_smbus_read_byte_data(client, reg);
else
return swab16(i2c_smbus_read_word_data(client, reg));
int ret;
ret = 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_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)
{
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 */
reg &= ~ DS1621_REG_CONFIG_1SHOT;
new_conf &= ~DS1621_REG_CONFIG_1SHOT;
/* setup output polarity */
if (polarity == 0)
reg &= ~DS1621_REG_CONFIG_POLARITY;
new_conf &= ~DS1621_REG_CONFIG_POLARITY;
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 */
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,
char *buf)
{
......@@ -160,12 +173,12 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
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));
mutex_lock(&data->update_lock);
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]);
mutex_unlock(&data->update_lock);
return count;
......@@ -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
requested during the latest 10ms, which is highly
improbable in our case. */
conf = ds1621_read_value(client, DS1621_REG_CONF);
if (conf & DS1621_REG_CONFIG_NVB)
conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF);
if (conf < 0 || conf & DS1621_REG_CONFIG_NVB)
return -ENODEV;
/* The 7 lowest bits of a temperature should always be 0. */
for (i = 0; i < ARRAY_SIZE(DS1621_REG_TEMP); i++) {
temp = ds1621_read_value(client, DS1621_REG_TEMP[i]);
if (temp & 0x007f)
temp = i2c_smbus_read_word_data(client,
DS1621_REG_TEMP[i]);
if (temp < 0 || (temp & 0x7f00))
return -ENODEV;
}
}
......@@ -294,45 +308,25 @@ static int ds1621_remove(struct i2c_client *client)
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)
{
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 = 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;
}
/* 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,
};
static int __init ds1621_init(void)
{
......
此差异已折叠。
......@@ -65,6 +65,10 @@
#define HDAPS_INPUT_FUZZ 4 /* input event threshold */
#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 input_polled_dev *hdaps_idev;
static unsigned int hdaps_invert;
......@@ -182,11 +186,11 @@ static int __hdaps_read_pair(unsigned int port1, unsigned int port2,
km_activity = inb(HDAPS_PORT_KMACT);
__device_complete();
/* if hdaps_invert is set, negate the two values */
if (hdaps_invert) {
/* hdaps_invert is a bitvector to negate the axes */
if (hdaps_invert & HDAPS_X_AXIS)
*x = -*x;
if (hdaps_invert & HDAPS_Y_AXIS)
*y = -*y;
}
return 0;
}
......@@ -436,7 +440,8 @@ static ssize_t hdaps_invert_store(struct device *dev,
{
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;
hdaps_invert = invert;
......@@ -483,56 +488,52 @@ static int __init hdaps_dmi_match(const struct dmi_system_id *id)
/* hdaps_dmi_match_invert - found an inverted match. */
static int __init hdaps_dmi_match_invert(const struct dmi_system_id *id)
{
hdaps_invert = 1;
printk(KERN_INFO "hdaps: inverting axis readings.\n");
hdaps_invert = (unsigned long)id->driver_data;
printk(KERN_INFO "hdaps: inverting axis (%u) readings.\n",
hdaps_invert);
return hdaps_dmi_match(id);
}
#define HDAPS_DMI_MATCH_NORMAL(vendor, model) { \
.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) { \
#define HDAPS_DMI_MATCH_INVERT(vendor, model, axes) { \
.ident = vendor " " model, \
.callback = hdaps_dmi_match_invert, \
.driver_data = (void *)axes, \
.matches = { \
DMI_MATCH(DMI_BOARD_VENDOR, vendor), \
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
"ThinkPad T42p", so the order of the entries matters.
If your ThinkPad is not recognized, please update to latest
BIOS. This is especially the case for some R52 ThinkPads. */
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 R51"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61i"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61"),
HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61i", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad R61", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p", HDAPS_BOTH_AXES),
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 T43"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61p", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T61", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X41"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61"),
HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad X41", HDAPS_Y_AXIS),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61s", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X61", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61m"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61p"),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61m", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad Z61p", HDAPS_BOTH_AXES),
{ .ident = NULL }
};
......@@ -627,8 +628,9 @@ static void __exit hdaps_exit(void)
module_init(hdaps_init);
module_exit(hdaps_exit);
module_param_named(invert, hdaps_invert, bool, 0);
MODULE_PARM_DESC(invert, "invert data along each axis");
module_param_named(invert, hdaps_invert, int, 0);
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_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver");
......
......@@ -36,6 +36,7 @@
w83627ehf 10 5 4 3 0x8850 0x88 0x5ca3
0x8860 0xa1
w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3
w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3
*/
#include <linux/module.h>
......@@ -52,12 +53,13 @@
#include <asm/io.h>
#include "lm75.h"
enum kinds { w83627ehf, w83627dhg };
enum kinds { w83627ehf, w83627dhg, w83667hg };
/* used to set data->name = w83627ehf_device_names[data->sio_kind] */
static const char * w83627ehf_device_names[] = {
"w83627ehf",
"w83627dhg",
"w83667hg",
};
static unsigned short force_id;
......@@ -71,6 +73,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID");
*/
#define W83627EHF_LD_HWM 0x0b
#define W83667HG_LD_VID 0x0d
#define SIO_REG_LDSEL 0x07 /* Logical device select */
#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
......@@ -83,6 +86,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID");
#define SIO_W83627EHF_ID 0x8850
#define SIO_W83627EHG_ID 0x8860
#define SIO_W83627DHG_ID 0xa020
#define SIO_W83667HG_ID 0xa510
#define SIO_ID_MASK 0xFFF0
static inline void
......@@ -289,6 +293,7 @@ struct w83627ehf_data {
u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */
u8 pwm_enable[4]; /* 1->manual
2->thermal cruise (also called SmartFan I) */
u8 pwm_num; /* number of pwm */
u8 pwm[4];
u8 target_temp[4];
u8 tolerance[4];
......@@ -298,6 +303,9 @@ struct w83627ehf_data {
u8 vid;
u8 vrm;
u8 temp3_disable;
u8 in6_skip;
};
struct w83627ehf_sio_data {
......@@ -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]);
}
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(temp2_input, S_IRUGO, show_temp, NULL, 0),
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,
store_temp1_max, 0),
SENSOR_ATTR(temp2_max, S_IRUGO | S_IWUSR, show_temp_max,
store_temp_max, 0),
SENSOR_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max,
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,
store_temp1_max_hyst, 0),
SENSOR_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst,
store_temp_max_hyst, 0),
SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst,
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(temp2_alarm, S_IRUGO, show_alarm, NULL, 5),
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(temp2_type, S_IRUGO, show_temp_type, NULL, 1),
SENSOR_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2),
......@@ -1181,6 +1201,8 @@ static void w83627ehf_device_remove_files(struct device *dev)
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
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_alarm[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)
device_remove_file(dev, &sda_fan_div[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_mode[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_tolerance[i].dev_attr);
}
for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
device_remove_file(dev, &sda_temp[i].dev_attr);
for (i = 0; i < 3; i++) {
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_cpu0_vid);
......@@ -1222,6 +1251,8 @@ static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data)
for (i = 0; i < 2; i++) {
tmp = w83627ehf_read_value(data,
W83627EHF_REG_TEMP_CONFIG[i]);
if ((i == 1) && data->temp3_disable)
continue;
if (tmp & 0x01)
w83627ehf_write_value(data,
W83627EHF_REG_TEMP_CONFIG[i],
......@@ -1272,8 +1303,17 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
data->name = w83627ehf_device_names[sio_data->kind];
platform_set_drvdata(pdev, data);
/* 627EHG and 627EHF have 10 voltage inputs; DHG has 9 */
data->in_num = (sio_data->kind == w83627dhg) ? 9 : 10;
/* 627EHG and 627EHF have 10 voltage inputs; 627DHG and 667HG have 9 */
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 */
w83627ehf_init_device(data);
......@@ -1281,29 +1321,44 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
data->vrm = vid_which_vrm();
superio_enter(sio_data->sioreg);
/* Read VID value */
if (sio_data->kind == w83667hg) {
/* W83667HG has different pins for VID input and output, so
we can get the VID input values directly at logical device D
0xe3. */
superio_select(sio_data->sioreg, W83667HG_LD_VID);
data->vid = superio_inb(sio_data->sioreg, 0xe3);
err = device_create_file(dev, &dev_attr_cpu0_vid);
if (err)
goto exit_release;
} else {
superio_select(sio_data->sioreg, W83627EHF_LD_HWM);
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. */
/* 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,
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,
} 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);
data->vid = superio_inb(sio_data->sioreg,
SIO_REG_VID_DATA);
if (sio_data->kind == w83627ehf) /* 6 VID pins only */
data->vid &= 0x3f;
......@@ -1314,11 +1369,16 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
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 */
fan5pin = superio_inb(sio_data->sioreg, 0x24) & 0x2;
fan4pin = superio_inb(sio_data->sioreg, 0x29) & 0x6;
if (sio_data->kind == w83667hg) {
fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20;
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);
/* It looks like fan4 and fan5 pins can be alternatively used
......@@ -1329,9 +1389,9 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
data->has_fan = 0x07; /* fan1, fan2 and fan3 */
i = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1);
if ((i & (1 << 2)) && (!fan4pin))
if ((i & (1 << 2)) && fan4pin)
data->has_fan |= (1 << 3);
if (!(i & (1 << 1)) && (!fan5pin))
if (!(i & (1 << 1)) && fan5pin)
data->has_fan |= (1 << 4);
/* Read fan clock dividers immediately */
......@@ -1344,14 +1404,16 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
goto exit_remove;
/* 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++) {
if ((err = device_create_file(dev,
&sda_sf3_arrays_fan4[i].dev_attr)))
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))
|| (err = device_create_file(dev,
&sda_in_alarm[i].dev_attr))
......@@ -1360,6 +1422,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
|| (err = device_create_file(dev,
&sda_in_max[i].dev_attr)))
goto exit_remove;
}
for (i = 0; i < 5; i++) {
if (data->has_fan & (1 << i)) {
......@@ -1372,7 +1435,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
|| (err = device_create_file(dev,
&sda_fan_min[i].dev_attr)))
goto exit_remove;
if (i < 4 && /* w83627ehf only has 4 pwm */
if (i < data->pwm_num &&
((err = device_create_file(dev,
&sda_pwm[i].dev_attr))
|| (err = device_create_file(dev,
......@@ -1387,9 +1450,21 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
}
}
for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
if ((err = device_create_file(dev, &sda_temp[i].dev_attr)))
for (i = 0; i < 3; i++) {
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;
}
err = device_create_file(dev, &dev_attr_name);
if (err)
......@@ -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_W83627EHG[] = "W83627EHG";
static const char __initdata sio_name_W83627DHG[] = "W83627DHG";
static const char __initdata sio_name_W83667HG[] = "W83667HG";
u16 val;
const char *sio_name;
......@@ -1466,6 +1542,10 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr,
sio_data->kind = w83627dhg;
sio_name = sio_name_W83627DHG;
break;
case SIO_W83667HG_ID:
sio_data->kind = w83667hg;
sio_name = sio_name_W83667HG;
break;
default:
if (val != 0xffff)
pr_debug(DRVNAME ": unsupported chip ID: 0x%04x\n",
......
......@@ -65,6 +65,7 @@
#include <linux/i2c.h>
#include <linux/acpi.h>
#include <linux/io.h>
#include <linux/dmi.h>
/* I801 SMBus address offsets */
#define SMBHSTSTS (0 + i801_smba)
......@@ -616,10 +617,81 @@ static void __init input_apanel_init(void)
static void __init input_apanel_init(void) {}
#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)
{
unsigned char temp;
int err;
#if defined CONFIG_SENSORS_FSCHMD || defined CONFIG_SENSORS_FSCHMD_MODULE
const char *vendor;
#endif
I801_dev = dev;
i801_features = 0;
......@@ -712,6 +784,11 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id
i2c_new_device(&i801_adapter, &info);
}
#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;
......
......@@ -64,19 +64,6 @@ config SENSORS_PCA9539
This driver is deprecated and will be dropped soon. Use
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
tristate "Maxim MAX6875 Power supply supervisor"
depends on EXPERIMENTAL
......
......@@ -15,7 +15,6 @@ obj-$(CONFIG_SENSORS_MAX6875) += max6875.o
obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o
obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
obj-$(CONFIG_PCF8575) += pcf8575.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
......
......@@ -103,7 +103,7 @@ static void parse_da_table(const struct dmi_header *dm)
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) {
case 0xd4: /* Indexed IO */
......@@ -356,7 +356,7 @@ static int __init dell_init(void)
if (!dmi_check_system(dell_device_table))
return -ENODEV;
dmi_walk(find_tokens);
dmi_walk(find_tokens, NULL);
if (!da_tokens) {
printk(KERN_INFO "dell-laptop: Unable to find dmi tokens\n");
......
......@@ -380,7 +380,7 @@ asm(".text \n\t"
* This function checks whether or not a SMBIOS/DMI record is
* 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;
unsigned long cru_physical_address;
......@@ -403,7 +403,7 @@ static int __devinit detect_cru_service(void)
{
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 */
return ((cru_rom_addr != NULL) ? 0 : -ENODEV);
......
......@@ -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_serial(const char *str);
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);
#else
......@@ -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_serial(const char *s) { return 0; }
#define dmi_available 0
static inline int dmi_walk(void (*decode)(const struct dmi_header *))
{ return -1; }
static inline int dmi_walk(void (*decode)(const struct dmi_header *, void *),
void *private_data) { return -1; }
static inline bool dmi_match(enum dmi_field f, const char *str)
{ return false; }
static inline const struct dmi_system_id *
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册