aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans de Goede <j.w.r.degoede@hhs.nl>2007-12-20 10:42:59 -0500
committerMark M. Hoffman <mhoffman@lightlink.com>2008-02-07 20:39:42 -0500
commit7845cd791d87b9d5e6171452143dbef15aba00dc (patch)
treec07576a2f0200742daf19c098c9ed0c8bd1b8f69
parent67b671bceb4a8340a30929e9642620d99ed5ad76 (diff)
hwmon: (fschmd) Read voltage scaling factors from BIOS DMI
This patch adds support to the fschmd driver for reading the voltage scaling factors from BIOS DMI tables, as specified in the Siemens datasheet. Signed-off-by: Hans de Goede <j.w.r.degoede@hhs.nl> Acked-by: Jean Delvare <khali@linux-fr.org> Signed-off-by: Mark M. Hoffman <mhoffman@lightlink.com>
-rw-r--r--drivers/hwmon/fschmd.c90
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 */
46static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; 47static 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. */
216static int dmi_mult[3] = { 490, 200, 100 };
217static int dmi_offset[3] = { 0, 0, 0 };
218static 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. */
543static 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
528static int fschmd_detect(struct i2c_adapter *adapter, int address, int kind) 603static 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);