diff options
Diffstat (limited to 'drivers/hwmon/fschmd.c')
-rw-r--r-- | drivers/hwmon/fschmd.c | 90 |
1 files changed, 88 insertions, 2 deletions
diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index d427ea5623fb..b7c9eef0f928 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include <linux/err.h> | 41 | #include <linux/err.h> |
42 | #include <linux/mutex.h> | 42 | #include <linux/mutex.h> |
43 | #include <linux/sysfs.h> | 43 | #include <linux/sysfs.h> |
44 | #include <linux/dmi.h> | ||
44 | 45 | ||
45 | /* Addresses to scan */ | 46 | /* Addresses to scan */ |
46 | static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; | 47 | static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; |
@@ -210,6 +211,13 @@ struct fschmd_data { | |||
210 | u8 fan_ripple[6]; /* divider for rps */ | 211 | u8 fan_ripple[6]; /* divider for rps */ |
211 | }; | 212 | }; |
212 | 213 | ||
214 | /* Global variables to hold information read from special DMI tables, which are | ||
215 | available on FSC machines with an fscher or later chip. */ | ||
216 | static int dmi_mult[3] = { 490, 200, 100 }; | ||
217 | static int dmi_offset[3] = { 0, 0, 0 }; | ||
218 | static int dmi_vref = -1; | ||
219 | |||
220 | |||
213 | /* | 221 | /* |
214 | * Sysfs attr show / store functions | 222 | * Sysfs attr show / store functions |
215 | */ | 223 | */ |
@@ -221,8 +229,13 @@ static ssize_t show_in_value(struct device *dev, | |||
221 | int index = to_sensor_dev_attr(devattr)->index; | 229 | int index = to_sensor_dev_attr(devattr)->index; |
222 | struct fschmd_data *data = fschmd_update_device(dev); | 230 | struct fschmd_data *data = fschmd_update_device(dev); |
223 | 231 | ||
224 | return sprintf(buf, "%d\n", (data->volt[index] * | 232 | /* fscher / fschrc - 1 as data->kind is an array index, not a chips */ |
225 | max_reading[index] + 128) / 255); | 233 | if (data->kind == (fscher - 1) || data->kind >= (fschrc - 1)) |
234 | return sprintf(buf, "%d\n", (data->volt[index] * dmi_vref * | ||
235 | dmi_mult[index]) / 255 + dmi_offset[index]); | ||
236 | else | ||
237 | return sprintf(buf, "%d\n", (data->volt[index] * | ||
238 | max_reading[index] + 128) / 255); | ||
226 | } | 239 | } |
227 | 240 | ||
228 | 241 | ||
@@ -525,6 +538,68 @@ static struct sensor_device_attribute fschmd_fan_attr[] = { | |||
525 | * Real code | 538 | * Real code |
526 | */ | 539 | */ |
527 | 540 | ||
541 | /* DMI decode routine to read voltage scaling factors from special DMI tables, | ||
542 | which are available on FSC machines with an fscher or later chip. */ | ||
543 | static void fschmd_dmi_decode(const struct dmi_header *header) | ||
544 | { | ||
545 | int i, mult[3] = { 0 }, offset[3] = { 0 }, vref = 0, found = 0; | ||
546 | |||
547 | /* dmi code ugliness, we get passed the address of the contents of | ||
548 | a complete DMI record, but in the form of a dmi_header pointer, in | ||
549 | reality this address holds header->length bytes of which the header | ||
550 | are the first 4 bytes */ | ||
551 | u8 *dmi_data = (u8 *)header; | ||
552 | |||
553 | /* We are looking for OEM-specific type 185 */ | ||
554 | if (header->type != 185) | ||
555 | return; | ||
556 | |||
557 | /* we are looking for what Siemens calls "subtype" 19, the subtype | ||
558 | is stored in byte 5 of the dmi block */ | ||
559 | if (header->length < 5 || dmi_data[4] != 19) | ||
560 | return; | ||
561 | |||
562 | /* After the subtype comes 1 unknown byte and then blocks of 5 bytes, | ||
563 | consisting of what Siemens calls an "Entity" number, followed by | ||
564 | 2 16-bit words in LSB first order */ | ||
565 | for (i = 6; (i + 4) < header->length; i += 5) { | ||
566 | /* entity 1 - 3: voltage multiplier and offset */ | ||
567 | if (dmi_data[i] >= 1 && dmi_data[i] <= 3) { | ||
568 | /* Our in sensors order and the DMI order differ */ | ||
569 | const int shuffle[3] = { 1, 0, 2 }; | ||
570 | int in = shuffle[dmi_data[i] - 1]; | ||
571 | |||
572 | /* Check for twice the same entity */ | ||
573 | if (found & (1 << in)) | ||
574 | return; | ||
575 | |||
576 | mult[in] = dmi_data[i + 1] | (dmi_data[i + 2] << 8); | ||
577 | offset[in] = dmi_data[i + 3] | (dmi_data[i + 4] << 8); | ||
578 | |||
579 | found |= 1 << in; | ||
580 | } | ||
581 | |||
582 | /* entity 7: reference voltage */ | ||
583 | if (dmi_data[i] == 7) { | ||
584 | /* Check for twice the same entity */ | ||
585 | if (found & 0x08) | ||
586 | return; | ||
587 | |||
588 | vref = dmi_data[i + 1] | (dmi_data[i + 2] << 8); | ||
589 | |||
590 | found |= 0x08; | ||
591 | } | ||
592 | } | ||
593 | |||
594 | if (found == 0x0F) { | ||
595 | for (i = 0; i < 3; i++) { | ||
596 | dmi_mult[i] = mult[i] * 10; | ||
597 | dmi_offset[i] = offset[i] * 10; | ||
598 | } | ||
599 | dmi_vref = vref; | ||
600 | } | ||
601 | } | ||
602 | |||
528 | static int fschmd_detect(struct i2c_adapter *adapter, int address, int kind) | 603 | static int fschmd_detect(struct i2c_adapter *adapter, int address, int kind) |
529 | { | 604 | { |
530 | struct i2c_client *client; | 605 | struct i2c_client *client; |
@@ -586,6 +661,17 @@ static int fschmd_detect(struct i2c_adapter *adapter, int address, int kind) | |||
586 | data->temp_max[2] = 50 + 128; | 661 | data->temp_max[2] = 50 + 128; |
587 | } | 662 | } |
588 | 663 | ||
664 | /* Read the special DMI table for fscher and newer chips */ | ||
665 | if (kind == fscher || kind >= fschrc) { | ||
666 | dmi_walk(fschmd_dmi_decode); | ||
667 | if (dmi_vref == -1) { | ||
668 | printk(KERN_WARNING FSCHMD_NAME | ||
669 | ": Couldn't get voltage scaling factors from " | ||
670 | "BIOS DMI table, using builtin defaults\n"); | ||
671 | dmi_vref = 33; | ||
672 | } | ||
673 | } | ||
674 | |||
589 | /* i2c kind goes from 1-5, we want from 0-4 to address arrays */ | 675 | /* i2c kind goes from 1-5, we want from 0-4 to address arrays */ |
590 | data->kind = kind - 1; | 676 | data->kind = kind - 1; |
591 | strlcpy(client->name, client_names[data->kind], I2C_NAME_SIZE); | 677 | strlcpy(client->name, client_names[data->kind], I2C_NAME_SIZE); |