summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <greg@kroah.com>2003-04-01 22:21:30 -0800
committerGreg Kroah-Hartman <greg@kroah.com>2003-04-01 22:21:30 -0800
commit37581a1c41b0dfa49ca8649a1ef472c1987def6d (patch)
tree9942a0b7d7d9f813338ca496d42316e7c90698ac
parent797886bf130a8721934bb4464464797e759da52b (diff)
parent7eaa539b232c505f09fd45776c5e2ba2ae08cc80 (diff)
Merge kroah.com:/home/greg/linux/BK/bleed-2.5
into kroah.com:/home/greg/linux/BK/i2c-2.5
-rw-r--r--drivers/i2c/Kconfig11
-rw-r--r--drivers/i2c/Makefile2
-rw-r--r--drivers/i2c/chips/Kconfig38
-rw-r--r--drivers/i2c/chips/Makefile2
-rw-r--r--drivers/i2c/chips/adm1021.c362
-rw-r--r--drivers/i2c/chips/lm75.c187
-rw-r--r--drivers/i2c/chips/via686a.c930
-rw-r--r--drivers/i2c/chips/w83781d.c1882
-rw-r--r--drivers/i2c/i2c-core.c190
-rw-r--r--drivers/i2c/i2c-proc.c733
-rw-r--r--drivers/i2c/i2c-sensor.c182
-rw-r--r--drivers/media/video/adv7175.c2
-rw-r--r--drivers/media/video/tvmixer.c20
-rw-r--r--include/linux/i2c-sensor.h (renamed from include/linux/i2c-proc.h)51
-rw-r--r--include/linux/i2c-vid.h62
15 files changed, 3359 insertions, 1295 deletions
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index a35646b9d747..9aedbad0267a 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -196,17 +196,6 @@ config I2C_CHARDEV
<file:Documentation/modules.txt>.
The module will be called i2c-dev.
-config I2C_PROC
- tristate "I2C /proc interface (required for hardware sensors)"
- depends on I2C && SYSCTL
- help
- This provides support for i2c device entries in the /proc filesystem.
- The entries will be found in /proc/sys/dev/sensors.
-
- This code is also available as a module. If you want to compile
- it as a module, say M here and read <file:Documentation/modules.txt>.
- The module will be called i2c-proc.
-
source drivers/i2c/busses/Kconfig
source drivers/i2c/chips/Kconfig
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 5d3253927c7a..12336ac4e751 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -14,5 +14,5 @@ obj-$(CONFIG_ITE_I2C_ALGO) += i2c-algo-ite.o
obj-$(CONFIG_ITE_I2C_ADAP) += i2c-adap-ite.o
obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o
obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
-obj-$(CONFIG_I2C_PROC) += i2c-proc.o
+obj-$(CONFIG_I2C_SENSOR) += i2c-sensor.o
obj-y += busses/ chips/
diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
index 76058d89e053..6748623a5ce4 100644
--- a/drivers/i2c/chips/Kconfig
+++ b/drivers/i2c/chips/Kconfig
@@ -1,13 +1,13 @@
#
# Sensor device configuration
-# All depend on EXPERIMENTAL, I2C and I2C_PROC.
+# All depend on EXPERIMENTAL and I2C
#
menu "I2C Hardware Sensors Chip support"
config SENSORS_ADM1021
tristate " Analog Devices ADM1021 and compatibles"
- depends on I2C && I2C_PROC
+ depends on I2C && EXPERIMENTAL
help
If you say yes here you get support for Analog Devices ADM1021
and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A,
@@ -24,7 +24,7 @@ config SENSORS_ADM1021
config SENSORS_LM75
tristate " National Semiconductors LM75 and compatibles"
- depends on I2C && I2C_PROC
+ depends on I2C && EXPERIMENTAL
help
If you say yes here you get support for National Semiconductor LM75
sensor chips and clones: Dallas Semi DS75 and DS1775, TelCon
@@ -36,5 +36,37 @@ config SENSORS_LM75
You will also need the latest user-space utilties: you can find them
in the lm_sensors package, which you can download at
http://www.lm-sensors.nu
+
+config SENSORS_VIA686A
+ tristate " VIA686A"
+ depends on I2C && EXPERIMENTAL
+ help
+ support for via686a
+ If you say yes here you get support for the integrated sensors in
+ Via 686A/B South Bridges. This can also be built as a module
+ which can be inserted and removed while the kernel is running.
+
+ You will also need the latest user-space utilties: you can find them
+ in the lm_sensors package, which you can download at
+ http://www.lm-sensors.nu
+
+config SENSORS_W83781D
+ tristate " Winbond W83781D, W83782D, W83783S, W83627HF, Asus AS99127F"
+ depends on I2C && EXPERIMENTAL
+ help
+ If you say yes here you get support for the Winbond W8378x series
+ of sensor chips: the W83781D, W83782D, W83783S and W83682HF,
+ and the similar Asus AS99127F. This
+ can also be built as a module which can be inserted and removed
+ while the kernel is running.
+
+ You will also need the latest user-space utilties: you can find them
+ in the lm_sensors package, which you can download at
+ http://www.lm-sensors.nu
+
+config I2C_SENSOR
+ tristate
+ depends on SENSORS_ADM1021 || SENSORS_LM75 || SENSORS_VIA686A || SENSORS_W83781D
+ default m
endmenu
diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
index 835c8db87b4f..cc7755134565 100644
--- a/drivers/i2c/chips/Makefile
+++ b/drivers/i2c/chips/Makefile
@@ -4,3 +4,5 @@
obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o
obj-$(CONFIG_SENSORS_LM75) += lm75.o
+obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
+obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
diff --git a/drivers/i2c/chips/adm1021.c b/drivers/i2c/chips/adm1021.c
index 16035bdfbecd..d35ae9ef54bc 100644
--- a/drivers/i2c/chips/adm1021.c
+++ b/drivers/i2c/chips/adm1021.c
@@ -23,7 +23,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
-#include <linux/i2c-proc.h>
+#include <linux/i2c-sensor.h>
/* Registers */
@@ -53,34 +53,34 @@ SENSORS_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1
/* The adm1021 registers */
/* Read-only */
-#define ADM1021_REG_TEMP 0x00
-#define ADM1021_REG_REMOTE_TEMP 0x01
-#define ADM1021_REG_STATUS 0x02
-#define ADM1021_REG_MAN_ID 0x0FE /* 0x41 = AMD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi*/
-#define ADM1021_REG_DEV_ID 0x0FF /* ADM1021 = 0x0X, ADM1023 = 0x3X */
-#define ADM1021_REG_DIE_CODE 0x0FF /* MAX1617A */
+#define ADM1021_REG_TEMP 0x00
+#define ADM1021_REG_REMOTE_TEMP 0x01
+#define ADM1021_REG_STATUS 0x02
+#define ADM1021_REG_MAN_ID 0x0FE /* 0x41 = AMD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi*/
+#define ADM1021_REG_DEV_ID 0x0FF /* ADM1021 = 0x0X, ADM1023 = 0x3X */
+#define ADM1021_REG_DIE_CODE 0x0FF /* MAX1617A */
/* These use different addresses for reading/writing */
-#define ADM1021_REG_CONFIG_R 0x03
-#define ADM1021_REG_CONFIG_W 0x09
-#define ADM1021_REG_CONV_RATE_R 0x04
-#define ADM1021_REG_CONV_RATE_W 0x0A
+#define ADM1021_REG_CONFIG_R 0x03
+#define ADM1021_REG_CONFIG_W 0x09
+#define ADM1021_REG_CONV_RATE_R 0x04
+#define ADM1021_REG_CONV_RATE_W 0x0A
/* These are for the ADM1023's additional precision on the remote temp sensor */
-#define ADM1021_REG_REM_TEMP_PREC 0x010
-#define ADM1021_REG_REM_OFFSET 0x011
-#define ADM1021_REG_REM_OFFSET_PREC 0x012
-#define ADM1021_REG_REM_TOS_PREC 0x013
-#define ADM1021_REG_REM_THYST_PREC 0x014
+#define ADM1021_REG_REM_TEMP_PREC 0x010
+#define ADM1021_REG_REM_OFFSET 0x011
+#define ADM1021_REG_REM_OFFSET_PREC 0x012
+#define ADM1021_REG_REM_TOS_PREC 0x013
+#define ADM1021_REG_REM_THYST_PREC 0x014
/* limits */
-#define ADM1021_REG_TOS_R 0x05
-#define ADM1021_REG_TOS_W 0x0B
-#define ADM1021_REG_REMOTE_TOS_R 0x07
-#define ADM1021_REG_REMOTE_TOS_W 0x0D
-#define ADM1021_REG_THYST_R 0x06
-#define ADM1021_REG_THYST_W 0x0C
-#define ADM1021_REG_REMOTE_THYST_R 0x08
-#define ADM1021_REG_REMOTE_THYST_W 0x0E
+#define ADM1021_REG_TOS_R 0x05
+#define ADM1021_REG_TOS_W 0x0B
+#define ADM1021_REG_REMOTE_TOS_R 0x07
+#define ADM1021_REG_REMOTE_TOS_W 0x0D
+#define ADM1021_REG_THYST_R 0x06
+#define ADM1021_REG_THYST_W 0x0C
+#define ADM1021_REG_REMOTE_THYST_R 0x08
+#define ADM1021_REG_REMOTE_THYST_W 0x0E
/* write-only */
-#define ADM1021_REG_ONESHOT 0x0F
+#define ADM1021_REG_ONESHOT 0x0F
/* Conversions. Rounding and limit checking is only done on the TO_REG
@@ -88,8 +88,8 @@ SENSORS_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1
these macros are called: arguments may be evaluated more than once.
Fixing this is just not worth it. */
/* Conversions note: 1021 uses normal integer signed-byte format*/
-#define TEMP_FROM_REG(val) (val > 127 ? val-256 : val)
-#define TEMP_TO_REG(val) (SENSORS_LIMIT((val < 0 ? val+256 : val),0,255))
+#define TEMP_FROM_REG(val) (val > 127 ? val-256 : val)
+#define TEMP_TO_REG(val) (SENSORS_LIMIT((val < 0 ? val+256 : val),0,255))
/* Initial values */
@@ -97,44 +97,43 @@ SENSORS_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1
they don't quite work like a thermostat the way the LM75 does. I.e.,
a lower temp than THYST actually triggers an alarm instead of
clearing it. Weird, ey? --Phil */
-#define adm1021_INIT_TOS 60
-#define adm1021_INIT_THYST 20
-#define adm1021_INIT_REMOTE_TOS 60
-#define adm1021_INIT_REMOTE_THYST 20
+#define adm1021_INIT_TOS 60
+#define adm1021_INIT_THYST 20
+#define adm1021_INIT_REMOTE_TOS 60
+#define adm1021_INIT_REMOTE_THYST 20
/* Each client has this additional data */
struct adm1021_data {
- int sysctl_id;
enum chips type;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
- u8 temp, temp_os, temp_hyst; /* Register values */
- u8 remote_temp, remote_temp_os, remote_temp_hyst, alarms, die_code;
+ u8 temp_max; /* Register values */
+ u8 temp_hyst;
+ u8 temp_input;
+ u8 remote_temp_max;
+ u8 remote_temp_hyst;
+ u8 remote_temp_input;
+ u8 alarms;
+ /* special values for ADM1021 only */
+ u8 die_code;
/* Special values for ADM1023 only */
- u8 remote_temp_prec, remote_temp_os_prec, remote_temp_hyst_prec,
- remote_temp_offset, remote_temp_offset_prec;
+ u8 remote_temp_prec;
+ u8 remote_temp_os_prec;
+ u8 remote_temp_hyst_prec;
+ u8 remote_temp_offset;
+ u8 remote_temp_offset_prec;
};
static int adm1021_attach_adapter(struct i2c_adapter *adapter);
-static int adm1021_detect(struct i2c_adapter *adapter, int address,
- unsigned short flags, int kind);
+static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind);
static void adm1021_init_client(struct i2c_client *client);
static int adm1021_detach_client(struct i2c_client *client);
static int adm1021_read_value(struct i2c_client *client, u8 reg);
static int adm1021_write_value(struct i2c_client *client, u8 reg,
u16 value);
-static void adm1021_temp(struct i2c_client *client, int operation,
- int ctl_name, int *nrels_mag, long *results);
-static void adm1021_remote_temp(struct i2c_client *client, int operation,
- int ctl_name, int *nrels_mag,
- long *results);
-static void adm1021_alarms(struct i2c_client *client, int operation,
- int ctl_name, int *nrels_mag, long *results);
-static void adm1021_die_code(struct i2c_client *client, int operation,
- int ctl_name, int *nrels_mag, long *results);
static void adm1021_update_client(struct i2c_client *client);
/* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */
@@ -151,45 +150,63 @@ static struct i2c_driver adm1021_driver = {
.detach_client = adm1021_detach_client,
};
-/* These files are created for each detected adm1021. This is just a template;
- though at first sight, you might think we could use a statically
- allocated list, we need some way to get back to the parent - which
- is done through one of the 'extra' fields which are initialized
- when a new copy is allocated. */
-static ctl_table adm1021_dir_table_template[] = {
- {ADM1021_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
- &i2c_sysctl_real, NULL, &adm1021_temp},
- {ADM1021_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
- &i2c_sysctl_real, NULL, &adm1021_remote_temp},
- {ADM1021_SYSCTL_DIE_CODE, "die_code", NULL, 0, 0444, NULL, &i2c_proc_real,
- &i2c_sysctl_real, NULL, &adm1021_die_code},
- {ADM1021_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
- &i2c_sysctl_real, NULL, &adm1021_alarms},
- {0}
-};
-
-static ctl_table adm1021_max_dir_table_template[] = {
- {ADM1021_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
- &i2c_sysctl_real, NULL, &adm1021_temp},
- {ADM1021_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
- &i2c_sysctl_real, NULL, &adm1021_remote_temp},
- {ADM1021_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real,
- &i2c_sysctl_real, NULL, &adm1021_alarms},
- {0}
-};
-
/* I choose here for semi-static allocation. Complete dynamic
allocation could also be used; the code needed for this would probably
take more memory than the datastructure takes now. */
static int adm1021_id = 0;
+#define show(value) \
+static ssize_t show_##value(struct device *dev, char *buf) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct adm1021_data *data = i2c_get_clientdata(client); \
+ int temp; \
+ \
+ adm1021_update_client(client); \
+ temp = TEMP_FROM_REG(data->value); \
+ return sprintf(buf, "%d\n", temp); \
+}
+show(temp_max);
+show(temp_hyst);
+show(temp_input);
+show(remote_temp_max);
+show(remote_temp_hyst);
+show(remote_temp_input);
+show(alarms);
+show(die_code);
+
+#define set(value, reg) \
+static ssize_t set_##value(struct device *dev, const char *buf, size_t count) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct adm1021_data *data = i2c_get_clientdata(client); \
+ int temp = simple_strtoul(buf, NULL, 10); \
+ \
+ data->value = TEMP_TO_REG(temp); \
+ adm1021_write_value(client, reg, data->value); \
+ return count; \
+}
+set(temp_max, ADM1021_REG_TOS_W);
+set(temp_hyst, ADM1021_REG_THYST_W);
+set(remote_temp_max, ADM1021_REG_REMOTE_TOS_W);
+set(remote_temp_hyst, ADM1021_REG_REMOTE_THYST_W);
+
+static DEVICE_ATTR(temp_max1, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max);
+static DEVICE_ATTR(temp_min1, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst);
+static DEVICE_ATTR(temp_input1, S_IRUGO, show_temp_input, NULL);
+static DEVICE_ATTR(temp_max2, S_IWUSR | S_IRUGO, show_remote_temp_max, set_remote_temp_max);
+static DEVICE_ATTR(temp_min2, S_IWUSR | S_IRUGO, show_remote_temp_hyst, set_remote_temp_hyst);
+static DEVICE_ATTR(temp_input2, S_IRUGO, show_remote_temp_input, NULL);
+static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static DEVICE_ATTR(die_code, S_IRUGO, show_die_code, NULL);
+
+
static int adm1021_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_detect(adapter, &addr_data, adm1021_detect);
}
-static int adm1021_detect(struct i2c_adapter *adapter, int address,
- unsigned short flags, int kind)
+static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i;
struct i2c_client *new_client;
@@ -202,8 +219,7 @@ static int adm1021_detect(struct i2c_adapter *adapter, int address,
at this moment; i2c_detect really won't call us. */
#ifdef DEBUG
if (i2c_is_isa_adapter(adapter)) {
- printk
- ("adm1021.o: adm1021_detect called for an ISA bus adapter?!?\n");
+ dev_dbg(&adapter->dev, "adm1021_detect called for an ISA bus adapter?!?\n");
return 0;
}
#endif
@@ -232,35 +248,28 @@ static int adm1021_detect(struct i2c_adapter *adapter, int address,
new_client->flags = 0;
/* Now, we do the remaining detection. */
-
if (kind < 0) {
- if (
- (adm1021_read_value(new_client, ADM1021_REG_STATUS) &
- 0x03) != 0x00)
+ if ((adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0x03) != 0x00)
goto error1;
}
/* Determine the chip type. */
-
if (kind <= 0) {
i = adm1021_read_value(new_client, ADM1021_REG_MAN_ID);
if (i == 0x41)
- if ((adm1021_read_value (new_client, ADM1021_REG_DEV_ID) & 0x0F0) == 0x030)
- kind = adm1023;
- else
- kind = adm1021;
+ if ((adm1021_read_value(new_client, ADM1021_REG_DEV_ID) & 0x0F0) == 0x030)
+ kind = adm1023;
+ else
+ kind = adm1021;
else if (i == 0x49)
kind = thmc10;
else if (i == 0x23)
kind = gl523sm;
else if ((i == 0x4d) &&
- (adm1021_read_value
- (new_client, ADM1021_REG_DEV_ID) == 0x01))
+ (adm1021_read_value(new_client, ADM1021_REG_DEV_ID) == 0x01))
kind = max1617a;
/* LM84 Mfr ID in a different place */
- else
- if (adm1021_read_value
- (new_client, ADM1021_REG_CONV_RATE_R) == 0x00)
+ else if (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) == 0x00)
kind = lm84;
else if (i == 0x54)
kind = mc1066;
@@ -293,10 +302,8 @@ static int adm1021_detect(struct i2c_adapter *adapter, int address,
type_name = "mc1066";
client_name = "MC1066 chip";
} else {
-#ifdef DEBUG
- printk("adm1021.o: Internal error: unknown kind (%d)?!?",
- kind);
-#endif
+ dev_err(&adapter->dev, "Internal error: unknown kind (%d)?!?",
+ kind);
goto error1;
}
@@ -312,25 +319,24 @@ static int adm1021_detect(struct i2c_adapter *adapter, int address,
if ((err = i2c_attach_client(new_client)))
goto error3;
- /* Register a new directory entry with module sensors */
- err = i2c_register_entry(new_client, type_name,
- (data->type == adm1021) ?
- adm1021_dir_table_template :
- adm1021_max_dir_table_template);
- if (err < 0)
- goto error4;
+ device_create_file(&new_client->dev, &dev_attr_temp_max1);
+ device_create_file(&new_client->dev, &dev_attr_temp_min1);
+ device_create_file(&new_client->dev, &dev_attr_temp_input1);
+ device_create_file(&new_client->dev, &dev_attr_temp_max2);
+ device_create_file(&new_client->dev, &dev_attr_temp_min2);
+ device_create_file(&new_client->dev, &dev_attr_temp_input2);
+ device_create_file(&new_client->dev, &dev_attr_alarms);
+ if (data->type == adm1021)
+ device_create_file(&new_client->dev, &dev_attr_die_code);
- data->sysctl_id = err;
/* Initialize the ADM1021 chip */
adm1021_init_client(new_client);
return 0;
- error4:
- i2c_detach_client(new_client);
- error3:
- error1:
+error3:
+error1:
kfree(new_client);
- error0:
+error0:
return err;
}
@@ -353,21 +359,15 @@ static void adm1021_init_client(struct i2c_client *client)
static int adm1021_detach_client(struct i2c_client *client)
{
-
int err;
- i2c_deregister_entry(((struct adm1021_data *) (i2c_get_clientdata(client)))->sysctl_id);
-
if ((err = i2c_detach_client(client))) {
- printk
- ("adm1021.o: Client deregistration failed, client not detached.\n");
+ dev_err(&client->dev, "Client deregistration failed, client not detached.\n");
return err;
}
kfree(client);
-
return 0;
-
}
/* All registers are byte-sized */
@@ -391,39 +391,23 @@ static void adm1021_update_client(struct i2c_client *client)
if ((jiffies - data->last_updated > HZ + HZ / 2) ||
(jiffies < data->last_updated) || !data->valid) {
-
-#ifdef DEBUG
- printk("Starting adm1021 update\n");
-#endif
-
- data->temp = adm1021_read_value(client, ADM1021_REG_TEMP);
- data->temp_os =
- adm1021_read_value(client, ADM1021_REG_TOS_R);
- data->temp_hyst =
- adm1021_read_value(client, ADM1021_REG_THYST_R);
- data->remote_temp =
- adm1021_read_value(client, ADM1021_REG_REMOTE_TEMP);
- data->remote_temp_os =
- adm1021_read_value(client, ADM1021_REG_REMOTE_TOS_R);
- data->remote_temp_hyst =
- adm1021_read_value(client, ADM1021_REG_REMOTE_THYST_R);
- data->alarms =
- adm1021_read_value(client, ADM1021_REG_STATUS) & 0xec;
+ dev_dbg(&client->dev, "Starting adm1021 update\n");
+
+ data->temp_input = adm1021_read_value(client, ADM1021_REG_TEMP);
+ data->temp_max = adm1021_read_value(client, ADM1021_REG_TOS_R);
+ data->temp_hyst = adm1021_read_value(client, ADM1021_REG_THYST_R);
+ data->remote_temp_input = adm1021_read_value(client, ADM1021_REG_REMOTE_TEMP);
+ data->remote_temp_max = adm1021_read_value(client, ADM1021_REG_REMOTE_TOS_R);
+ data->remote_temp_hyst = adm1021_read_value(client, ADM1021_REG_REMOTE_THYST_R);
+ data->alarms = adm1021_read_value(client, ADM1021_REG_STATUS) & 0xec;
if (data->type == adm1021)
- data->die_code =
- adm1021_read_value(client,
- ADM1021_REG_DIE_CODE);
+ data->die_code = adm1021_read_value(client, ADM1021_REG_DIE_CODE);
if (data->type == adm1023) {
- data->remote_temp_prec =
- adm1021_read_value(client, ADM1021_REG_REM_TEMP_PREC);
- data->remote_temp_os_prec =
- adm1021_read_value(client, ADM1021_REG_REM_TOS_PREC);
- data->remote_temp_hyst_prec =
- adm1021_read_value(client, ADM1021_REG_REM_THYST_PREC);
- data->remote_temp_offset =
- adm1021_read_value(client, ADM1021_REG_REM_OFFSET);
- data->remote_temp_offset_prec =
- adm1021_read_value(client, ADM1021_REG_REM_OFFSET_PREC);
+ data->remote_temp_prec = adm1021_read_value(client, ADM1021_REG_REM_TEMP_PREC);
+ data->remote_temp_os_prec = adm1021_read_value(client, ADM1021_REG_REM_TOS_PREC);
+ data->remote_temp_hyst_prec = adm1021_read_value(client, ADM1021_REG_REM_THYST_PREC);
+ data->remote_temp_offset = adm1021_read_value(client, ADM1021_REG_REM_OFFSET);
+ data->remote_temp_offset_prec = adm1021_read_value(client, ADM1021_REG_REM_OFFSET_PREC);
}
data->last_updated = jiffies;
data->valid = 1;
@@ -433,6 +417,9 @@ static void adm1021_update_client(struct i2c_client *client)
}
+/* FIXME, remove these four functions, they are here to verify the sysfs
+ * conversion is correct, or not */
+__attribute__((unused))
static void adm1021_temp(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
@@ -442,15 +429,15 @@ static void adm1021_temp(struct i2c_client *client, int operation,
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
adm1021_update_client(client);
- results[0] = TEMP_FROM_REG(data->temp_os);
+ results[0] = TEMP_FROM_REG(data->temp_max);
results[1] = TEMP_FROM_REG(data->temp_hyst);
- results[2] = TEMP_FROM_REG(data->temp);
+ results[2] = TEMP_FROM_REG(data->temp_input);
*nrels_mag = 3;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
- data->temp_os = TEMP_TO_REG(results[0]);
+ data->temp_max = TEMP_TO_REG(results[0]);
adm1021_write_value(client, ADM1021_REG_TOS_W,
- data->temp_os);
+ data->temp_max);
}
if (*nrels_mag >= 2) {
data->temp_hyst = TEMP_TO_REG(results[1]);
@@ -460,6 +447,7 @@ static void adm1021_temp(struct i2c_client *client, int operation,
}
}
+__attribute__((unused))
static void adm1021_remote_temp(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
@@ -471,68 +459,53 @@ static void adm1021_remote_temp(struct i2c_client *client, int operation,
else { *nrels_mag = 0; }
else if (operation == SENSORS_PROC_REAL_READ) {
adm1021_update_client(client);
- results[0] = TEMP_FROM_REG(data->remote_temp_os);
+ results[0] = TEMP_FROM_REG(data->remote_temp_max);
results[1] = TEMP_FROM_REG(data->remote_temp_hyst);
- results[2] = TEMP_FROM_REG(data->remote_temp);
+ results[2] = TEMP_FROM_REG(data->remote_temp_input);
if (data->type == adm1023) {
- results[0]=results[0]*1000 +
- ((data->remote_temp_os_prec >> 5) * 125);
- results[1]=results[1]*1000 +
- ((data->remote_temp_hyst_prec >> 5) * 125);
- results[2]=(TEMP_FROM_REG(data->remote_temp_offset)*1000) +
- ((data->remote_temp_offset_prec >> 5) * 125);
- results[3]=TEMP_FROM_REG(data->remote_temp)*1000 +
- ((data->remote_temp_prec >> 5) * 125);
- *nrels_mag = 4;
+ results[0] = results[0]*1000 + ((data->remote_temp_os_prec >> 5) * 125);
+ results[1] = results[1]*1000 + ((data->remote_temp_hyst_prec >> 5) * 125);
+ results[2] = (TEMP_FROM_REG(data->remote_temp_offset)*1000) + ((data->remote_temp_offset_prec >> 5) * 125);
+ results[3] = (TEMP_FROM_REG(data->remote_temp_input)*1000) + ((data->remote_temp_prec >> 5) * 125);
+ *nrels_mag = 4;
} else {
- *nrels_mag = 3;
+ *nrels_mag = 3;
}
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
if (data->type == adm1023) {
- prec=((results[0]-((results[0]/1000)*1000))/125)<<5;
- adm1021_write_value(client,
- ADM1021_REG_REM_TOS_PREC,
- prec);
- results[0]=results[0]/1000;
- data->remote_temp_os_prec=prec;
+ prec = ((results[0]-((results[0]/1000)*1000))/125)<<5;
+ adm1021_write_value(client, ADM1021_REG_REM_TOS_PREC, prec);
+ results[0] = results[0]/1000;
+ data->remote_temp_os_prec=prec;
}
- data->remote_temp_os = TEMP_TO_REG(results[0]);
- adm1021_write_value(client,
- ADM1021_REG_REMOTE_TOS_W,
- data->remote_temp_os);
+ data->remote_temp_max = TEMP_TO_REG(results[0]);
+ adm1021_write_value(client, ADM1021_REG_REMOTE_TOS_W, data->remote_temp_max);
}
if (*nrels_mag >= 2) {
if (data->type == adm1023) {
- prec=((results[1]-((results[1]/1000)*1000))/125)<<5;
- adm1021_write_value(client,
- ADM1021_REG_REM_THYST_PREC,
- prec);
- results[1]=results[1]/1000;
- data->remote_temp_hyst_prec=prec;
+ prec = ((results[1]-((results[1]/1000)*1000))/125)<<5;
+ adm1021_write_value(client, ADM1021_REG_REM_THYST_PREC, prec);
+ results[1] = results[1]/1000;
+ data->remote_temp_hyst_prec=prec;
}
data->remote_temp_hyst = TEMP_TO_REG(results[1]);
- adm1021_write_value(client,
- ADM1021_REG_REMOTE_THYST_W,
- data->remote_temp_hyst);
+ adm1021_write_value(client, ADM1021_REG_REMOTE_THYST_W, data->remote_temp_hyst);
}
if (*nrels_mag >= 3) {
if (data->type == adm1023) {
- prec=((results[2]-((results[2]/1000)*1000))/125)<<5;
- adm1021_write_value(client,
- ADM1021_REG_REM_OFFSET_PREC,
- prec);
- results[2]=results[2]/1000;
- data->remote_temp_offset_prec=prec;
- data->remote_temp_offset=results[2];
- adm1021_write_value(client,
- ADM1021_REG_REM_OFFSET,
- data->remote_temp_offset);
+ prec = ((results[2]-((results[2]/1000)*1000))/125)<<5;
+ adm1021_write_value(client, ADM1021_REG_REM_OFFSET_PREC, prec);
+ results[2]=results[2]/1000;
+ data->remote_temp_offset_prec=prec;
+ data->remote_temp_offset=results[2];
+ adm1021_write_value(client, ADM1021_REG_REM_OFFSET, data->remote_temp_offset);
}
}
}
}
+__attribute__((unused))
static void adm1021_die_code(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
@@ -549,6 +522,7 @@ static void adm1021_die_code(struct i2c_client *client, int operation,
}
}
+__attribute__((unused))
static void adm1021_alarms(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
@@ -574,8 +548,8 @@ static void __exit sensors_adm1021_exit(void)
i2c_del_driver(&adm1021_driver);
}
-MODULE_AUTHOR
- ("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl> and "
+ "Philip Edelbrock <phil@netroedge.com>");
MODULE_DESCRIPTION("adm1021 driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/chips/lm75.c b/drivers/i2c/chips/lm75.c
index 4b2f315b75da..f17984cf6ab8 100644
--- a/drivers/i2c/chips/lm75.c
+++ b/drivers/i2c/chips/lm75.c
@@ -18,14 +18,14 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+/* #define DEBUG 1 */
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
-#include <linux/i2c-proc.h>
-
+#include <linux/i2c-sensor.h>
-#define LM75_SYSCTL_TEMP 1200 /* Degrees Celsius * 10 */
/* Addresses to scan */
static unsigned short normal_i2c[] = { SENSORS_I2C_END };
@@ -39,97 +39,113 @@ SENSORS_INSMOD_1(lm75);
/* Many LM75 constants specified below */
/* The LM75 registers */
-#define LM75_REG_TEMP 0x00
-#define LM75_REG_CONF 0x01
-#define LM75_REG_TEMP_HYST 0x02
-#define LM75_REG_TEMP_OS 0x03
+#define LM75_REG_TEMP 0x00
+#define LM75_REG_CONF 0x01
+#define LM75_REG_TEMP_HYST 0x02
+#define LM75_REG_TEMP_OS 0x03
/* Conversions. Rounding and limit checking is only done on the TO_REG
variants. Note that you should be a bit careful with which arguments
these macros are called: arguments may be evaluated more than once.
Fixing this is just not worth it. */
-#define TEMP_FROM_REG(val) ((((val & 0x7fff) >> 7) * 5) | ((val & 0x8000)?-256:0))
-#define TEMP_TO_REG(val) (SENSORS_LIMIT((val<0?(0x200+((val)/5))<<7:(((val) + 2) / 5) << 7),0,0xffff))
+#define TEMP_FROM_REG(val) ((((val & 0x7fff) >> 7) * 5) | ((val & 0x8000)?-256:0))
+#define TEMP_TO_REG(val) (SENSORS_LIMIT((val<0?(0x200+((val)/5))<<7:(((val) + 2) / 5) << 7),0,0xffff))
/* Initial values */
-#define LM75_INIT_TEMP_OS 600
-#define LM75_INIT_TEMP_HYST 500
+#define LM75_INIT_TEMP_OS 600
+#define LM75_INIT_TEMP_HYST 500
/* Each client has this additional data */
struct lm75_data {
- int sysctl_id;
-
- struct semaphore update_lock;
- char valid; /* !=0 if following fields are valid */
- unsigned long last_updated; /* In jiffies */
-
- u16 temp, temp_os, temp_hyst; /* Register values */
+ struct semaphore update_lock;
+ char valid; /* !=0 if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+ u16 temp_input; /* Register values */
+ u16 temp_max;
+ u16 temp_hyst;
};
static int lm75_attach_adapter(struct i2c_adapter *adapter);
-static int lm75_detect(struct i2c_adapter *adapter, int address,
- unsigned short flags, int kind);
+static int lm75_detect(struct i2c_adapter *adapter, int address, int kind);
static void lm75_init_client(struct i2c_client *client);
static int lm75_detach_client(struct i2c_client *client);
-static u16 swap_bytes(u16 val);
static int lm75_read_value(struct i2c_client *client, u8 reg);
static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value);
-static void lm75_temp(struct i2c_client *client, int operation,
- int ctl_name, int *nrels_mag, long *results);
static void lm75_update_client(struct i2c_client *client);
/* This is the driver that will be inserted */
static struct i2c_driver lm75_driver = {
.owner = THIS_MODULE,
- .name = "LM75 sensor",
+ .name = "lm75",
.id = I2C_DRIVERID_LM75,
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm75_attach_adapter,
.detach_client = lm75_detach_client,
};
-/* These files are created for each detected LM75. This is just a template;
- though at first sight, you might think we could use a statically
- allocated list, we need some way to get back to the parent - which
- is done through one of the 'extra' fields which are initialized
- when a new copy is allocated. */
-static ctl_table lm75_dir_table_template[] = {
- {LM75_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real,
- &i2c_sysctl_real, NULL, &lm75_temp},
- {0}
-};
-
static int lm75_id = 0;
+#define show(value) \
+static ssize_t show_##value(struct device *dev, char *buf) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct lm75_data *data = i2c_get_clientdata(client); \
+ int temp; \
+ \
+ lm75_update_client(client); \
+ temp = TEMP_FROM_REG(data->value); \
+ return sprintf(buf, "%d\n", temp * 100); \
+}
+show(temp_max);
+show(temp_hyst);
+show(temp_input);
+
+#define set(value, reg) \
+static ssize_t set_##value(struct device *dev, const char *buf, size_t count) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct lm75_data *data = i2c_get_clientdata(client); \
+ int temp = simple_strtoul(buf, NULL, 10) / 100; \
+ \
+ data->value = TEMP_TO_REG(temp); \
+ lm75_write_value(client, reg, data->value); \
+ return count; \
+}
+set(temp_max, LM75_REG_TEMP_OS);
+set(temp_hyst, LM75_REG_TEMP_HYST);
+
+static DEVICE_ATTR(temp_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max);
+static DEVICE_ATTR(temp_min, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst);
+static DEVICE_ATTR(temp_input, S_IRUGO, show_temp_input, NULL);
+
static int lm75_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_detect(adapter, &addr_data, lm75_detect);
}
/* This function is called by i2c_detect */
-static int lm75_detect(struct i2c_adapter *adapter, int address,
- unsigned short flags, int kind)
+static int lm75_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i, cur, conf, hyst, os;
struct i2c_client *new_client;
struct lm75_data *data;
int err = 0;
- const char *type_name, *client_name;
+ const char *name;
/* Make sure we aren't probing the ISA bus!! This is just a safety check
at this moment; i2c_detect really won't call us. */
#ifdef DEBUG
if (i2c_is_isa_adapter(adapter)) {
- printk
- ("lm75.o: lm75_detect called for an ISA bus adapter?!?\n");
- return 0;
+ dev_dbg(&adapter->dev,
+ "lm75_detect called for an ISA bus adapter?!?\n");
+ goto exit;
}
#endif
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
- goto error0;
+ goto exit;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
@@ -138,7 +154,7 @@ static int lm75_detect(struct i2c_adapter *adapter, int address,
sizeof(struct lm75_data),
GFP_KERNEL))) {
err = -ENOMEM;
- goto error0;
+ goto exit;
}
memset(new_client, 0x00, sizeof(struct i2c_client) +
sizeof(struct lm75_data));
@@ -157,16 +173,10 @@ static int lm75_detect(struct i2c_adapter *adapter, int address,
hyst = i2c_smbus_read_word_data(new_client, 2);
os = i2c_smbus_read_word_data(new_client, 3);
for (i = 0; i <= 0x1f; i++)
- if (
- (i2c_smbus_read_byte_data
- (new_client, i * 8 + 1) != conf)
- ||
- (i2c_smbus_read_word_data
- (new_client, i * 8 + 2) != hyst)
- ||
- (i2c_smbus_read_word_data
- (new_client, i * 8 + 3) != os))
- goto error1;
+ if ((i2c_smbus_read_byte_data(new_client, i * 8 + 1) != conf) ||
+ (i2c_smbus_read_word_data(new_client, i * 8 + 2) != hyst) ||
+ (i2c_smbus_read_word_data(new_client, i * 8 + 3) != os))
+ goto exit_free;
}
/* Determine the chip type - only one kind supported! */
@@ -174,15 +184,15 @@ static int lm75_detect(struct i2c_adapter *adapter, int address,
kind = lm75;
if (kind == lm75) {
- type_name = "lm75";
- client_name = "LM75 chip";
+ name = "lm75";
} else {
- pr_debug("lm75.o: Internal error: unknown kind (%d)?!?", kind);
- goto error1;
+ dev_dbg(&adapter->dev, "Internal error: unknown kind (%d)?!?",
+ kind);
+ goto exit_free;
}
/* Fill in the remaining client fields and put it into the global list */
- strncpy(new_client->dev.name, client_name, DEVICE_NAME_SIZE);
+ strncpy(new_client->dev.name, name, DEVICE_NAME_SIZE);
new_client->id = lm75_id++;
data->valid = 0;
@@ -190,36 +200,23 @@ static int lm75_detect(struct i2c_adapter *adapter, int address,
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
- goto error3;
+ goto exit_free;
- /* Register a new directory entry with module sensors */
- i = i2c_register_entry(new_client, type_name, lm75_dir_table_template);
- if (i < 0) {
- err = i;
- goto error4;
- }
- data->sysctl_id = i;
+ device_create_file(&new_client->dev, &dev_attr_temp_max);
+ device_create_file(&new_client->dev, &dev_attr_temp_min);
+ device_create_file(&new_client->dev, &dev_attr_temp_input);
lm75_init_client(new_client);
return 0;
-/* OK, this is not exactly good programming practice, usually. But it is
- very code-efficient in this case. */
-
- error4:
- i2c_detach_client(new_client);
- error3:
- error1:
+exit_free:
kfree(new_client);
- error0:
+exit:
return err;
}
static int lm75_detach_client(struct i2c_client *client)
{
- struct lm75_data *data = i2c_get_clientdata(client);
-
- i2c_deregister_entry(data->sysctl_id);
i2c_detach_client(client);
kfree(client);
return 0;
@@ -271,12 +268,11 @@ static void lm75_update_client(struct i2c_client *client)
if ((jiffies - data->last_updated > HZ + HZ / 2) ||
(jiffies < data->last_updated) || !data->valid) {
- pr_debug("Starting lm75 update\n");
+ dev_dbg(&client->dev, "Starting lm75 update\n");
- data->temp = lm75_read_value(client, LM75_REG_TEMP);
- data->temp_os = lm75_read_value(client, LM75_REG_TEMP_OS);
- data->temp_hyst =
- lm75_read_value(client, LM75_REG_TEMP_HYST);
+ data->temp_input = lm75_read_value(client, LM75_REG_TEMP);
+ data->temp_max = lm75_read_value(client, LM75_REG_TEMP_OS);
+ data->temp_hyst = lm75_read_value(client, LM75_REG_TEMP_HYST);
data->last_updated = jiffies;
data->valid = 1;
}
@@ -284,33 +280,6 @@ static void lm75_update_client(struct i2c_client *client)
up(&data->update_lock);
}
-
-static void lm75_temp(struct i2c_client *client, int operation, int ctl_name,
- int *nrels_mag, long *results)
-{
- struct lm75_data *data = i2c_get_clientdata(client);
- if (operation == SENSORS_PROC_REAL_INFO)
- *nrels_mag = 1;
- else if (operation == SENSORS_PROC_REAL_READ) {
- lm75_update_client(client);
- results[0] = TEMP_FROM_REG(data->temp_os);
- results[1] = TEMP_FROM_REG(data->temp_hyst);
- results[2] = TEMP_FROM_REG(data->temp);
- *nrels_mag = 3;
- } else if (operation == SENSORS_PROC_REAL_WRITE) {
- if (*nrels_mag >= 1) {
- data->temp_os = TEMP_TO_REG(results[0]);
- lm75_write_value(client, LM75_REG_TEMP_OS,
- data->temp_os);
- }
- if (*nrels_mag >= 2) {
- data->temp_hyst = TEMP_TO_REG(results[1]);
- lm75_write_value(client, LM75_REG_TEMP_HYST,
- data->temp_hyst);
- }
- }
-}
-
static int __init sensors_lm75_init(void)
{
return i2c_add_driver(&lm75_driver);
diff --git a/drivers/i2c/chips/via686a.c b/drivers/i2c/chips/via686a.c
new file mode 100644
index 000000000000..f19893d1d204
--- /dev/null
+++ b/drivers/i2c/chips/via686a.c
@@ -0,0 +1,930 @@
+/*
+ via686a.c - Part of lm_sensors, Linux kernel modules
+ for hardware monitoring
+
+ Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
+ Kyösti Mälkki <kmalkki@cc.hut.fi>,
+ Mark Studebaker <mdsxyz123@yahoo.com>,
+ and Bob Dougherty <bobd@stanford.edu>
+ (Some conversion-factor data were contributed by Jonathan Teh Soon Yew
+ <j.teh@iname.com> and Alex van Kaam <darkside@chello.nl>.)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ Supports the Via VT82C686A, VT82C686B south bridges.
+ Reports all as a 686A.
+ See doc/chips/via686a for details.
+ Warning - only supports a single device.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+#include <linux/init.h>
+#include <asm/io.h>
+
+
+/* If force_addr is set to anything different from 0, we forcibly enable
+ the device at the given address. */
+static int force_addr = 0;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+ "Initialize the base address of the sensors");
+
+/* Addresses to scan.
+ Note that we can't determine the ISA address until we have initialized
+ our module */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
+static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(via686a);
+
+/*
+ The Via 686a southbridge has a LM78-like chip integrated on the same IC.
+ This driver is a customized copy of lm78.c
+*/
+
+/* Many VIA686A constants specified below */
+
+/* Length of ISA address segment */
+#define VIA686A_EXTENT 0x80
+#define VIA686A_BASE_REG 0x70
+#define VIA686A_ENABLE_REG 0x74
+
+/* The VIA686A registers */
+/* ins numbered 0-4 */
+#define VIA686A_REG_IN_MAX(nr) (0x2b + ((nr) * 2))
+#define VIA686A_REG_IN_MIN(nr) (0x2c + ((nr) * 2))
+#define VIA686A_REG_IN(nr) (0x22 + (nr))
+
+/* fans numbered 1-2 */
+#define VIA686A_REG_FAN_MIN(nr) (0x3a + (nr))
+#define VIA686A_REG_FAN(nr) (0x28 + (nr))
+
+/* the following values are as speced by VIA: */
+static const u8 regtemp[] = { 0x20, 0x21, 0x1f };
+static const u8 regover[] = { 0x39, 0x3d, 0x1d };
+static const u8 reghyst[] = { 0x3a, 0x3e, 0x1e };
+
+/* temps numbered 1-3 */
+#define VIA686A_REG_TEMP(nr) (regtemp[(nr) - 1])
+#define VIA686A_REG_TEMP_OVER(nr) (regover[(nr) - 1])
+#define VIA686A_REG_TEMP_HYST(nr) (reghyst[(nr) - 1])
+#define VIA686A_REG_TEMP_LOW1 0x4b // bits 7-6
+#define VIA686A_REG_TEMP_LOW23 0x49 // 2 = bits 5-4, 3 = bits 7-6
+
+#define VIA686A_REG_ALARM1 0x41
+#define VIA686A_REG_ALARM2 0x42
+#define VIA686A_REG_FANDIV 0x47
+#define VIA686A_REG_CONFIG 0x40
+/* The following register sets temp interrupt mode (bits 1-0 for temp1,
+ 3-2 for temp2, 5-4 for temp3). Modes are:
+ 00 interrupt stays as long as value is out-of-range
+ 01 interrupt is cleared once register is read (default)
+ 10 comparator mode- like 00, but ignores hysteresis
+ 11 same as 00 */
+#define VIA686A_REG_TEMP_MODE 0x4b
+/* We'll just assume that you want to set all 3 simultaneously: */
+#define VIA686A_TEMP_MODE_MASK 0x3F
+#define VIA686A_TEMP_MODE_CONTINUOUS (0x00)
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+ variants.
+
+********* VOLTAGE CONVERSIONS (Bob Dougherty) ********
+ From HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew):
+ voltagefactor[0]=1.25/2628; (2628/1.25=2102.4) // Vccp
+ voltagefactor[1]=1.25/2628; (2628/1.25=2102.4) // +2.5V
+ voltagefactor[2]=1.67/2628; (2628/1.67=1573.7) // +3.3V
+ voltagefactor[3]=2.6/2628; (2628/2.60=1010.8) // +5V
+ voltagefactor[4]=6.3/2628; (2628/6.30=417.14) // +12V
+ in[i]=(data[i+2]*25.0+133)*voltagefactor[i];
+ That is:
+ volts = (25*regVal+133)*factor
+ regVal = (volts/factor-133)/25
+ (These conversions were contributed by Jonathan Teh Soon Yew
+ <j.teh@iname.com>)
+
+ These get us close, but they don't completely agree with what my BIOS
+ says- they are all a bit low. But, it all we have to go on... */
+static inline u8 IN_TO_REG(long val, int inNum)
+{
+ /* to avoid floating point, we multiply everything by 100.
+ val is guaranteed to be positive, so we can achieve the effect of
+ rounding by (...*10+5)/10. Note that the *10 is hidden in the
+ /250 (which should really be /2500).
+ At the end, we need to /100 because we *100 everything and we need
+ to /10 because of the rounding thing, so we /1000. */
+ if (inNum <= 1)
+ return (u8)
+ SENSORS_LIMIT(((val * 210240 - 13300) / 250 + 5) / 1000,
+ 0, 255);
+ else if (inNum == 2)
+ return (u8)
+ SENSORS_LIMIT(((val * 157370 - 13300) / 250 + 5) / 1000,
+ 0, 255);
+ else if (inNum == 3)
+ return (u8)
+ SENSORS_LIMIT(((val * 101080 - 13300) / 250 + 5) / 1000,
+ 0, 255);
+ else
+ return (u8) SENSORS_LIMIT(((val * 41714 - 13300) / 250 + 5)
+ / 1000, 0, 255);
+}
+
+static inline long IN_FROM_REG(u8 val, int inNum)
+{
+ /* to avoid floating point, we multiply everything by 100.
+ val is guaranteed to be positive, so we can achieve the effect of
+ rounding by adding 0.5. Or, to avoid fp math, we do (...*10+5)/10.
+ We need to scale with *100 anyway, so no need to /100 at the end. */
+ if (inNum <= 1)
+ return (long) (((250000 * val + 13300) / 210240 * 10 + 5) /10);
+ else if (inNum == 2)
+ return (long) (((250000 * val + 13300) / 157370 * 10 + 5) /10);
+ else if (inNum == 3)
+ return (long) (((250000 * val + 13300) / 101080 * 10 + 5) /10);
+ else
+ return (long) (((250000 * val + 13300) / 41714 * 10 + 5) /10);
+}
+
+/********* FAN RPM CONVERSIONS ********/
+/* Higher register values = slower fans (the fan's strobe gates a counter).
+ But this chip saturates back at 0, not at 255 like all the other chips.
+ So, 0 means 0 RPM */
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+ if (rpm == 0)
+ return 0;
+ rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+ return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 255);
+}
+
+#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1350000/((val)*(div)))
+
+/******** TEMP CONVERSIONS (Bob Dougherty) *********/
+/* linear fits from HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew)
+ if(temp<169)
+ return double(temp)*0.427-32.08;
+ else if(temp>=169 && temp<=202)
+ return double(temp)*0.582-58.16;
+ else
+ return double(temp)*0.924-127.33;
+
+ A fifth-order polynomial fits the unofficial data (provided by Alex van
+ Kaam <darkside@chello.nl>) a bit better. It also give more reasonable
+ numbers on my machine (ie. they agree with what my BIOS tells me).
+ Here's the fifth-order fit to the 8-bit data:
+ temp = 1.625093e-10*val^5 - 1.001632e-07*val^4 + 2.457653e-05*val^3 -
+ 2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0.
+
+ (2000-10-25- RFD: thanks to Uwe Andersen <uandersen@mayah.com> for
+ finding my typos in this formula!)
+
+ Alas, none of the elegant function-fit solutions will work because we
+ aren't allowed to use floating point in the kernel and doing it with
+ integers doesn't rpovide enough precision. So we'll do boring old
+ look-up table stuff. The unofficial data (see below) have effectively
+ 7-bit resolution (they are rounded to the nearest degree). I'm assuming
+ that the transfer function of the device is monotonic and smooth, so a
+ smooth function fit to the data will allow us to get better precision.
+ I used the 5th-order poly fit described above and solved for
+ VIA register values 0-255. I *10 before rounding, so we get tenth-degree
+ precision. (I could have done all 1024 values for our 10-bit readings,
+ but the function is very linear in the useful range (0-80 deg C), so
+ we'll just use linear interpolation for 10-bit readings.) So, tempLUT
+ is the temp at via register values 0-255: */
+static const long tempLUT[] =
+ { -709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519,
+ -503, -487, -471, -456, -442, -428, -414, -400, -387, -375,
+ -362, -350, -339, -327, -316, -305, -295, -285, -275, -265,
+ -255, -246, -237, -229, -220, -212, -204, -196, -188, -180,
+ -173, -166, -159, -152, -145, -139, -132, -126, -120, -114,
+ -108, -102, -96, -91, -85, -80, -74, -69, -64, -59, -54, -49,
+ -44, -39, -34, -29, -25, -20, -15, -11, -6, -2, 3, 7, 12, 16,
+ 20, 25, 29, 33, 37, 42, 46, 50, 54, 59, 63, 67, 71, 75, 79, 84,
+ 88, 92, 96, 100, 104, 109, 113, 117, 121, 125, 130, 134, 138,
+ 142, 146, 151, 155, 159, 163, 168, 172, 176, 181, 185, 189,
+ 193, 198, 202, 206, 211, 215, 219, 224, 228, 232, 237, 241,
+ 245, 250, 254, 259, 263, 267, 272, 276, 281, 285, 290, 294,
+ 299, 303, 307, 312, 316, 321, 325, 330, 334, 339, 344, 348,
+ 353, 357, 362, 366, 371, 376, 380, 385, 390, 395, 399, 404,
+ 409, 414, 419, 423, 428, 433, 438, 443, 449, 454, 459, 464,
+ 469, 475, 480, 486, 491, 497, 502, 508, 514, 520, 526, 532,
+ 538, 544, 551, 557, 564, 571, 578, 584, 592, 599, 606, 614,
+ 621, 629, 637, 645, 654, 662, 671, 680, 689, 698, 708, 718,
+ 728, 738, 749, 759, 770, 782, 793, 805, 818, 830, 843, 856,
+ 870, 883, 898, 912, 927, 943, 958, 975, 991, 1008, 1026, 1044,
+ 1062, 1081, 1101, 1121, 1141, 1162, 1184, 1206, 1229, 1252,
+ 1276, 1301, 1326, 1352, 1378, 1406, 1434, 1462
+};
+
+/* the original LUT values from Alex van Kaam <darkside@chello.nl>
+ (for via register values 12-240):
+{-50,-49,-47,-45,-43,-41,-39,-38,-37,-35,-34,-33,-32,-31,
+-30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15,
+-15,-14,-14,-13,-12,-12,-11,-11,-10,-9,-9,-8,-8,-7,-7,-6,-6,-5,-5,-4,-4,-3,
+-3,-2,-2,-1,-1,0,0,1,1,1,3,3,3,4,4,4,5,5,5,6,6,7,7,8,8,9,9,9,10,10,11,11,12,
+12,12,13,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,21,21,21,22,22,
+22,23,23,24,24,25,25,26,26,26,27,27,27,28,28,29,29,30,30,30,31,31,32,32,33,
+33,34,34,35,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45,
+45,46,46,47,48,48,49,49,50,51,51,52,52,53,53,54,55,55,56,57,57,58,59,59,60,
+61,62,62,63,64,65,66,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,83,84,
+85,86,88,89,91,92,94,96,97,99,101,103,105,107,109,110};
+
+
+ Here's the reverse LUT. I got it by doing a 6-th order poly fit (needed
+ an extra term for a good fit to these inverse data!) and then
+ solving for each temp value from -50 to 110 (the useable range for
+ this chip). Here's the fit:
+ viaRegVal = -1.160370e-10*val^6 +3.193693e-08*val^5 - 1.464447e-06*val^4
+ - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01)
+ Note that n=161: */
+static const u8 viaLUT[] =
+ { 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40,
+ 41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66,
+ 69, 71, 73, 75, 77, 79, 82, 84, 86, 88, 91, 93, 95, 98, 100,
+ 103, 105, 107, 110, 112, 115, 117, 119, 122, 124, 126, 129,
+ 131, 134, 136, 138, 140, 143, 145, 147, 150, 152, 154, 156,
+ 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180,
+ 182, 183, 185, 187, 188, 190, 192, 193, 195, 196, 198, 199,
+ 200, 202, 203, 205, 206, 207, 208, 209, 210, 211, 212, 213,
+ 214, 215, 216, 217, 218, 219, 220, 221, 222, 222, 223, 224,
+ 225, 226, 226, 227, 228, 228, 229, 230, 230, 231, 232, 232,
+ 233, 233, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239,
+ 239, 240
+};
+
+/* Converting temps to (8-bit) hyst and over registers
+ No interpolation here. Just check the limits and go.
+ The +5 effectively rounds off properly and the +50 is because
+ the temps start at -50 */
+static inline u8 TEMP_TO_REG(long val)
+{
+ return (u8)
+ SENSORS_LIMIT(viaLUT[((val <= -500) ? 0 : (val >= 1100) ? 160 :
+ ((val + 5) / 10 + 50))], 0, 255);
+}
+
+/* for 8-bit temperature hyst and over registers
+ The temp values are already *10, so we don't need to do that.
+ But we _will_ round these off to the nearest degree with (...*10+5)/10 */
+#define TEMP_FROM_REG(val) ((tempLUT[(val)]*10+5)/10)
+
+/* for 10-bit temperature readings
+ You might _think_ this is too long to inline, but's it's really only
+ called once... */
+static inline long TEMP_FROM_REG10(u16 val)
+{
+ /* the temp values are already *10, so we don't need to do that. */
+ long temp;
+ u16 eightBits = val >> 2;
+ u16 twoBits = val & 3;
+
+ /* handle the extremes first (they won't interpolate well! ;-) */
+ if (val == 0)
+ return (long) tempLUT[0];
+ if (val == 1023)
+ return (long) tempLUT[255];
+
+ if (twoBits == 0)
+ return (long) tempLUT[eightBits];
+ else {
+ /* do some interpolation by multipying the lower and upper
+ bounds by 25, 50 or 75, then /100. */
+ temp = ((25 * (4 - twoBits)) * tempLUT[eightBits]
+ + (25 * twoBits) * tempLUT[eightBits + 1]);
+ /* increase the magnitude by 50 to achieve rounding. */
+ if (temp > 0)
+ temp += 50;
+ else
+ temp -= 50;
+ return (temp / 100);
+ }
+}
+
+#define ALARMS_FROM_REG(val) (val)
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
+
+/* Initial limits */
+#define VIA686A_INIT_IN_0 200
+#define VIA686A_INIT_IN_1 250
+#define VIA686A_INIT_IN_2 330
+#define VIA686A_INIT_IN_3 500
+#define VIA686A_INIT_IN_4 1200
+
+#define VIA686A_INIT_IN_PERCENTAGE 10
+
+#define VIA686A_INIT_IN_MIN_0 (VIA686A_INIT_IN_0 - VIA686A_INIT_IN_0 \
+ * VIA686A_INIT_IN_PERCENTAGE / 100)
+#define VIA686A_INIT_IN_MAX_0 (VIA686A_INIT_IN_0 + VIA686A_INIT_IN_0 \
+ * VIA686A_INIT_IN_PERCENTAGE / 100)
+#define VIA686A_INIT_IN_MIN_1 (VIA686A_INIT_IN_1 - VIA686A_INIT_IN_1 \
+ * VIA686A_INIT_IN_PERCENTAGE / 100)
+#define VIA686A_INIT_IN_MAX_1 (VIA686A_INIT_IN_1 + VIA686A_INIT_IN_1 \
+ * VIA686A_INIT_IN_PERCENTAGE / 100)
+#define VIA686A_INIT_IN_MIN_2 (VIA686A_INIT_IN_2 - VIA686A_INIT_IN_2 \
+ * VIA686A_INIT_IN_PERCENTAGE / 100)
+#define VIA686A_INIT_IN_MAX_2 (VIA686A_INIT_IN_2 + VIA686A_INIT_IN_2 \
+ * VIA686A_INIT_IN_PERCENTAGE / 100)
+#define VIA686A_INIT_IN_MIN_3 (VIA686A_INIT_IN_3 - VIA686A_INIT_IN_3 \
+ * VIA686A_INIT_IN_PERCENTAGE / 100)
+#define VIA686A_INIT_IN_MAX_3 (VIA686A_INIT_IN_3 + VIA686A_INIT_IN_3 \
+ * VIA686A_INIT_IN_PERCENTAGE / 100)
+#define VIA686A_INIT_IN_MIN_4 (VIA686A_INIT_IN_4 - VIA686A_INIT_IN_4 \
+ * VIA686A_INIT_IN_PERCENTAGE / 100)
+#define VIA686A_INIT_IN_MAX_4 (VIA686A_INIT_IN_4 + VIA686A_INIT_IN_4 \
+ * VIA686A_INIT_IN_PERCENTAGE / 100)
+
+#define VIA686A_INIT_FAN_MIN 3000
+
+#define VIA686A_INIT_TEMP_OVER 600
+#define VIA686A_INIT_TEMP_HYST 500
+
+/* For the VIA686A, we need to keep some data in memory. That
+ data is pointed to by via686a_list[NR]->data. The structure itself is
+ dynamically allocated, at the same time when a new via686a client is
+ allocated. */
+struct via686a_data {
+ struct semaphore update_lock;
+ char valid; /* !=0 if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ u8 in[5]; /* Register value */
+ u8 in_max[5]; /* Register value */
+ u8 in_min[5]; /* Register value */
+ u8 fan[2]; /* Register value */
+ u8 fan_min[2]; /* Register value */
+ u16 temp[3]; /* Register value 10 bit */
+ u8 temp_over[3]; /* Register value */
+ u8 temp_hyst[3]; /* Register value */
+ u8 fan_div[2]; /* Register encoding, shifted right */
+ u16 alarms; /* Register encoding, combined */
+};
+
+static struct pci_dev *s_bridge; /* pointer to the (only) via686a */
+
+static int via686a_attach_adapter(struct i2c_adapter *adapter);
+static int via686a_detect(struct i2c_adapter *adapter, int address, int kind);
+static int via686a_detach_client(struct i2c_client *client);
+
+static int via686a_read_value(struct i2c_client *client, u8 register);
+static void via686a_write_value(struct i2c_client *client, u8 register,
+ u8 value);
+static void via686a_update_client(struct i2c_client *client);
+static void via686a_init_client(struct i2c_client *client);
+
+static int via686a_id = 0;
+
+/* The driver. I choose to use type i2c_driver, as at is identical to both
+ smbus_driver and isa_driver, and clients could be of either kind */
+static struct i2c_driver via686a_driver = {
+ .owner = THIS_MODULE,
+ .name = "VIA686A",
+ .id = I2C_DRIVERID_VIA686A,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = via686a_attach_adapter,
+ .detach_client = via686a_detach_client,
+};
+
+
+
+/* The /proc/sys entries */
+
+/* -- SENSORS SYSCTL START -- */
+#define VIA686A_SYSCTL_IN0 1000
+#define VIA686A_SYSCTL_IN1 1001
+#define VIA686A_SYSCTL_IN2 1002
+#define VIA686A_SYSCTL_IN3 1003
+#define VIA686A_SYSCTL_IN4 1004
+#define VIA686A_SYSCTL_FAN1 1101
+#define VIA686A_SYSCTL_FAN2 1102
+#define VIA686A_SYSCTL_TEMP 1200
+#define VIA686A_SYSCTL_TEMP2 1201
+#define VIA686A_SYSCTL_TEMP3 1202
+#define VIA686A_SYSCTL_FAN_DIV 2000
+#define VIA686A_SYSCTL_ALARMS 2001
+
+#define VIA686A_ALARM_IN0 0x01
+#define VIA686A_ALARM_IN1 0x02
+#define VIA686A_ALARM_IN2 0x04
+#define VIA686A_ALARM_IN3 0x08
+#define VIA686A_ALARM_TEMP 0x10
+#define VIA686A_ALARM_FAN1 0x40
+#define VIA686A_ALARM_FAN2 0x80
+#define VIA686A_ALARM_IN4 0x100
+#define VIA686A_ALARM_TEMP2 0x800
+#define VIA686A_ALARM_CHAS 0x1000
+#define VIA686A_ALARM_TEMP3 0x8000
+
+/* -- SENSORS SYSCTL END -- */
+
+#if 0
+/* These files are created for each detected VIA686A. This is just a template;
+ though at first sight, you might think we could use a statically
+ allocated list, we need some way to get back to the parent - which
+ is done through one of the 'extra' fields which are initialized
+ when a new copy is allocated. */
+static ctl_table via686a_dir_table_template[] = {
+ {VIA686A_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
+ &i2c_sysctl_real, NULL, &via686a_in},
+ {VIA686A_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
+ &i2c_sysctl_real, NULL, &via686a_in},
+ {VIA686A_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
+ &i2c_sysctl_real, NULL, &via686a_in},
+ {VIA686A_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
+ &i2c_sysctl_real, NULL, &via686a_in},
+ {VIA686A_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
+ &i2c_sysctl_real, NULL, &via686a_in},
+ {VIA686A_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
+ &i2c_sysctl_real, NULL, &via686a_fan},
+ {VIA686A_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
+ &i2c_sysctl_real, NULL, &via686a_fan},
+ {VIA686A_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
+ &i2c_sysctl_real, NULL, &via686a_temp},
+ {VIA686A_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL,
+ &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_temp},
+ {VIA686A_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL,
+ &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_temp},
+ {VIA686A_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL,
+ &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_fan_div},
+ {VIA686A_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
+ &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_alarms},
+ {0}
+};
+#endif
+
+static inline int via686a_read_value(struct i2c_client *client, u8 reg)
+{
+ return (inb_p(client->addr + reg));
+}
+
+static inline void via686a_write_value(struct i2c_client *client, u8 reg,
+ u8 value)
+{
+ outb_p(value, client->addr + reg);
+}
+
+/* This is called when the module is loaded */
+static int via686a_attach_adapter(struct i2c_adapter *adapter)
+{
+ return i2c_detect(adapter, &addr_data, via686a_detect);
+}
+
+static int via686a_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+ struct i2c_client *new_client;
+ struct via686a_data *data;
+ int err = 0;
+ const char *name = "via686a";
+ u16 val;
+
+ /* Make sure we are probing the ISA bus!! */
+ if (!i2c_is_isa_adapter(adapter)) {
+ dev_err(&adapter->dev,
+ "via686a_detect called for an I2C bus adapter?!?\n");
+ return 0;
+ }
+
+ /* 8231 requires multiple of 256, we enforce that on 686 as well */
+ if(force_addr)
+ address = force_addr & 0xFF00;
+
+ if(force_addr) {
+ dev_warn(&adapter->dev,"forcing ISA address 0x%04X\n", address);
+ if (PCIBIOS_SUCCESSFUL !=
+ pci_write_config_word(s_bridge, VIA686A_BASE_REG, address))
+ return -ENODEV;
+ }
+ if (PCIBIOS_SUCCESSFUL !=
+ pci_read_config_word(s_bridge, VIA686A_ENABLE_REG, &val))
+ return -ENODEV;
+ if (!(val & 0x0001)) {
+ dev_warn(&adapter->dev,"enabling sensors\n");
+ if (PCIBIOS_SUCCESSFUL !=
+ pci_write_config_word(s_bridge, VIA686A_ENABLE_REG,
+ val | 0x0001))
+ return -ENODEV;
+ }
+
+ /* Reserve the ISA region */
+ if (!request_region(address, VIA686A_EXTENT, "via686a-sensor")) {
+ dev_err(&adapter->dev,"region 0x%x already in use!\n",
+ address);
+ return -ENODEV;
+ }
+
+ if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+ sizeof(struct via686a_data),
+ GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto ERROR0;
+ }
+
+ memset(new_client,0x00, sizeof(struct i2c_client) +
+ sizeof(struct via686a_data));
+ data = (struct via686a_data *) (new_client + 1);
+ i2c_set_clientdata(new_client, data);
+ new_client->addr = address;
+ new_client->adapter = adapter;
+ new_client->driver = &via686a_driver;
+ new_client->flags = 0;
+
+ /* Fill in the remaining client fields and put into the global list */
+ snprintf(new_client->dev.name, DEVICE_NAME_SIZE, name);
+
+ new_client->id = via686a_id++;
+ data->valid = 0;
+ init_MUTEX(&data->update_lock);
+ /* Tell the I2C layer a new client has arrived */
+ if ((err = i2c_attach_client(new_client)))
+ goto ERROR3;
+
+ /* Initialize the VIA686A chip */
+ via686a_init_client(new_client);
+ return 0;
+
+ ERROR3:
+ release_region(address, VIA686A_EXTENT);
+ kfree(new_client);
+ ERROR0:
+ return err;
+}
+
+static int via686a_detach_client(struct i2c_client *client)
+{
+ int err;
+
+ if ((err = i2c_detach_client(client))) {
+ dev_err(&client->dev,
+ "Client deregistration failed, client not detached.\n");
+ return err;
+ }
+
+ release_region(client->addr, VIA686A_EXTENT);
+ kfree(client);
+
+ return 0;
+}
+
+/* Called when we have found a new VIA686A. Set limits, etc. */
+static void via686a_init_client(struct i2c_client *client)
+{
+ int i;
+
+ /* Reset the device */
+ via686a_write_value(client, VIA686A_REG_CONFIG, 0x80);
+
+ /* Have to wait for reset to complete or else the following
+ initializations won't work reliably. The delay was arrived at
+ empirically, the datasheet doesn't tell you.
+ Waiting for the reset bit to clear doesn't work, it
+ clears in about 2-4 udelays and that isn't nearly enough. */
+ udelay(50);
+
+ via686a_write_value(client, VIA686A_REG_IN_MIN(0),
+ IN_TO_REG(VIA686A_INIT_IN_MIN_0, 0));
+ via686a_write_value(client, VIA686A_REG_IN_MAX(0),
+ IN_TO_REG(VIA686A_INIT_IN_MAX_0, 0));
+ via686a_write_value(client, VIA686A_REG_IN_MIN(1),
+ IN_TO_REG(VIA686A_INIT_IN_MIN_1, 1));
+ via686a_write_value(client, VIA686A_REG_IN_MAX(1),
+ IN_TO_REG(VIA686A_INIT_IN_MAX_1, 1));
+ via686a_write_value(client, VIA686A_REG_IN_MIN(2),
+ IN_TO_REG(VIA686A_INIT_IN_MIN_2, 2));
+ via686a_write_value(client, VIA686A_REG_IN_MAX(2),
+ IN_TO_REG(VIA686A_INIT_IN_MAX_2, 2));
+ via686a_write_value(client, VIA686A_REG_IN_MIN(3),
+ IN_TO_REG(VIA686A_INIT_IN_MIN_3, 3));
+ via686a_write_value(client, VIA686A_REG_IN_MAX(3),
+ IN_TO_REG(VIA686A_INIT_IN_MAX_3, 3));
+ via686a_write_value(client, VIA686A_REG_IN_MIN(4),
+ IN_TO_REG(VIA686A_INIT_IN_MIN_4, 4));
+ via686a_write_value(client, VIA686A_REG_IN_MAX(4),
+ IN_TO_REG(VIA686A_INIT_IN_MAX_4, 4));
+ via686a_write_value(client, VIA686A_REG_FAN_MIN(1),
+ FAN_TO_REG(VIA686A_INIT_FAN_MIN, 2));
+ via686a_write_value(client, VIA686A_REG_FAN_MIN(2),
+ FAN_TO_REG(VIA686A_INIT_FAN_MIN, 2));
+ for (i = 1; i <= 3; i++) {
+ via686a_write_value(client, VIA686A_REG_TEMP_OVER(i),
+ TEMP_TO_REG(VIA686A_INIT_TEMP_OVER));
+ via686a_write_value(client, VIA686A_REG_TEMP_HYST(i),
+ TEMP_TO_REG(VIA686A_INIT_TEMP_HYST));
+ }
+
+ /* Start monitoring */
+ via686a_write_value(client, VIA686A_REG_CONFIG, 0x01);
+
+ /* Cofigure temp interrupt mode for continuous-interrupt operation */
+ via686a_write_value(client, VIA686A_REG_TEMP_MODE,
+ via686a_read_value(client, VIA686A_REG_TEMP_MODE) &
+ !(VIA686A_TEMP_MODE_MASK | VIA686A_TEMP_MODE_CONTINUOUS));
+}
+
+static void via686a_update_client(struct i2c_client *client)
+{
+ struct via686a_data *data = i2c_get_clientdata(client);
+ int i;
+
+ down(&data->update_lock);
+
+ if ((jiffies - data->last_updated > HZ + HZ / 2) ||
+ (jiffies < data->last_updated) || !data->valid) {
+
+ for (i = 0; i <= 4; i++) {
+ data->in[i] =
+ via686a_read_value(client, VIA686A_REG_IN(i));
+ data->in_min[i] = via686a_read_value(client,
+ VIA686A_REG_IN_MIN
+ (i));
+ data->in_max[i] =
+ via686a_read_value(client, VIA686A_REG_IN_MAX(i));
+ }
+ for (i = 1; i <= 2; i++) {
+ data->fan[i - 1] =
+ via686a_read_value(client, VIA686A_REG_FAN(i));
+ data->fan_min[i - 1] = via686a_read_value(client,
+ VIA686A_REG_FAN_MIN(i));
+ }
+ for (i = 1; i <= 3; i++) {
+ data->temp[i - 1] = via686a_read_value(client,
+ VIA686A_REG_TEMP(i)) << 2;
+ data->temp_over[i - 1] =
+ via686a_read_value(client,
+ VIA686A_REG_TEMP_OVER(i));
+ data->temp_hyst[i - 1] =
+ via686a_read_value(client,
+ VIA686A_REG_TEMP_HYST(i));
+ }
+ /* add in lower 2 bits
+ temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1
+ temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23
+ temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23
+ */
+ data->temp[0] |= (via686a_read_value(client,
+ VIA686A_REG_TEMP_LOW1)
+ & 0xc0) >> 6;
+ data->temp[1] |=
+ (via686a_read_value(client, VIA686A_REG_TEMP_LOW23) &
+ 0x30) >> 4;
+ data->temp[2] |=
+ (via686a_read_value(client, VIA686A_REG_TEMP_LOW23) &
+ 0xc0) >> 6;
+
+ i = via686a_read_value(client, VIA686A_REG_FANDIV);
+ data->fan_div[0] = (i >> 4) & 0x03;
+ data->fan_div[1] = i >> 6;
+ data->alarms =
+ via686a_read_value(client,
+ VIA686A_REG_ALARM1) |
+ (via686a_read_value(client, VIA686A_REG_ALARM2) << 8);
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+
+ up(&data->update_lock);
+}
+
+
+/* The next few functions are the call-back functions of the /proc/sys and
+ sysctl files. Which function is used is defined in the ctl_table in
+ the extra1 field.
+ Each function must return the magnitude (power of 10 to divide the date
+ with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
+ put a maximum of *nrels elements in results reflecting the data of this
+ file, and set *nrels to the number it actually put in it, if operation==
+ SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
+ results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
+ Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
+ large enough (by checking the incoming value of *nrels). This is not very
+ good practice, but as long as you put less than about 5 values in results,
+ you can assume it is large enough. */
+/* FIXME, remove these functions, they are here to verify the sysfs conversion
+ * is correct, or not */
+__attribute__((unused))
+static void via686a_in(struct i2c_client *client, int operation, int ctl_name,
+ int *nrels_mag, long *results)
+{
+ struct via686a_data *data = i2c_get_clientdata(client);
+ int nr = ctl_name - VIA686A_SYSCTL_IN0;
+
+ if (operation == SENSORS_PROC_REAL_INFO)
+ *nrels_mag = 2;
+ else if (operation == SENSORS_PROC_REAL_READ) {
+ via686a_update_client(client);
+ results[0] = IN_FROM_REG(data->in_min[nr], nr);
+ results[1] = IN_FROM_REG(data->in_max[nr], nr);
+ results[2] = IN_FROM_REG(data->in[nr], nr);
+ *nrels_mag = 3;
+ } else if (operation == SENSORS_PROC_REAL_WRITE) {
+ if (*nrels_mag >= 1) {
+ data->in_min[nr] = IN_TO_REG(results[0], nr);
+ via686a_write_value(client, VIA686A_REG_IN_MIN(nr),
+ data->in_min[nr]);
+ }
+ if (*nrels_mag >= 2) {
+ data->in_max[nr] = IN_TO_REG(results[1], nr);
+ via686a_write_value(client, VIA686A_REG_IN_MAX(nr),
+ data->in_max[nr]);
+ }
+ }
+}
+
+__attribute__((unused))
+static void via686a_fan(struct i2c_client *client, int operation, int ctl_name,
+ int *nrels_mag, long *results)
+{
+ struct via686a_data *data = i2c_get_clientdata(client);
+ int nr = ctl_name - VIA686A_SYSCTL_FAN1 + 1;
+
+ if (operation == SENSORS_PROC_REAL_INFO)
+ *nrels_mag = 0;
+ else if (operation == SENSORS_PROC_REAL_READ) {
+ via686a_update_client(client);
+ results[0] = FAN_FROM_REG(data->fan_min[nr - 1],
+ DIV_FROM_REG(data->fan_div
+ [nr - 1]));
+ results[1] = FAN_FROM_REG(data->fan[nr - 1],
+ DIV_FROM_REG(data->fan_div[nr - 1]));
+ *nrels_mag = 2;
+ } else if (operation == SENSORS_PROC_REAL_WRITE) {
+ if (*nrels_mag >= 1) {
+ data->fan_min[nr - 1] = FAN_TO_REG(results[0],
+ DIV_FROM_REG(data->
+ fan_div[nr -1]));
+ via686a_write_value(client,
+ VIA686A_REG_FAN_MIN(nr),
+ data->fan_min[nr - 1]);
+ }
+ }
+}
+
+__attribute__((unused))
+static void via686a_temp(struct i2c_client *client, int operation, int ctl_name,
+ int *nrels_mag, long *results)
+{
+ struct via686a_data *data = i2c_get_clientdata(client);
+ int nr = ctl_name - VIA686A_SYSCTL_TEMP;
+
+ if (operation == SENSORS_PROC_REAL_INFO)
+ *nrels_mag = 1;
+ else if (operation == SENSORS_PROC_REAL_READ) {
+ via686a_update_client(client);
+ results[0] = TEMP_FROM_REG(data->temp_over[nr]);
+ results[1] = TEMP_FROM_REG(data->temp_hyst[nr]);
+ results[2] = TEMP_FROM_REG10(data->temp[nr]);
+ *nrels_mag = 3;
+ } else if (operation == SENSORS_PROC_REAL_WRITE) {
+ if (*nrels_mag >= 1) {
+ data->temp_over[nr] = TEMP_TO_REG(results[0]);
+ via686a_write_value(client,
+ VIA686A_REG_TEMP_OVER(nr + 1),
+ data->temp_over[nr]);
+ }
+ if (*nrels_mag >= 2) {
+ data->temp_hyst[nr] = TEMP_TO_REG(results[1]);
+ via686a_write_value(client,
+ VIA686A_REG_TEMP_HYST(nr + 1),
+ data->temp_hyst[nr]);
+ }
+ }
+}
+
+__attribute__((unused))
+static void via686a_alarms(struct i2c_client *client, int operation, int ctl_name,
+ int *nrels_mag, long *results)
+{
+ struct via686a_data *data = i2c_get_clientdata(client);
+ if (operation == SENSORS_PROC_REAL_INFO)
+ *nrels_mag = 0;
+ else if (operation == SENSORS_PROC_REAL_READ) {
+ via686a_update_client(client);
+ results[0] = ALARMS_FROM_REG(data->alarms);
+ *nrels_mag = 1;
+ }
+}
+
+__attribute__((unused))
+static void via686a_fan_div(struct i2c_client *client, int operation,
+ int ctl_name, int *nrels_mag, long *results)
+{
+ struct via686a_data *data = i2c_get_clientdata(client);
+ int old;
+
+ if (operation == SENSORS_PROC_REAL_INFO)
+ *nrels_mag = 0;
+ else if (operation == SENSORS_PROC_REAL_READ) {
+ via686a_update_client(client);
+ results[0] = DIV_FROM_REG(data->fan_div[0]);
+ results[1] = DIV_FROM_REG(data->fan_div[1]);
+ *nrels_mag = 2;
+ } else if (operation == SENSORS_PROC_REAL_WRITE) {
+ old = via686a_read_value(client, VIA686A_REG_FANDIV);
+ if (*nrels_mag >= 2) {
+ data->fan_div[1] = DIV_TO_REG(results[1]);
+ old = (old & 0x3f) | (data->fan_div[1] << 6);
+ }
+ if (*nrels_mag >= 1) {
+ data->fan_div[0] = DIV_TO_REG(results[0]);
+ old = (old & 0xcf) | (data->fan_div[0] << 4);
+ via686a_write_value(client, VIA686A_REG_FANDIV,
+ old);
+ }
+ }
+}
+
+
+static struct pci_device_id via686a_pci_ids[] __devinitdata = {
+ {
+ .vendor = PCI_VENDOR_ID_VIA,
+ .device = PCI_DEVICE_ID_VIA_82C686_4,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .class = 0,
+ .class_mask = 0,
+ .driver_data = 0,
+ },
+ { 0, }
+};
+
+static int __devinit via686a_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ u16 val;
+ int addr = 0;
+
+ if (PCIBIOS_SUCCESSFUL !=
+ pci_read_config_word(dev, VIA686A_BASE_REG, &val))
+ return -ENODEV;
+
+ addr = val & ~(VIA686A_EXTENT - 1);
+ if (addr == 0 && force_addr == 0) {
+ dev_err(&dev->dev,"base address not set - upgrade BIOS or use force_addr=0xaddr\n");
+ return -ENODEV;
+ }
+ if (force_addr)
+ addr = force_addr; /* so detect will get called */
+
+ if (!addr) {
+ dev_err(&dev->dev,"No Via 686A sensors found.\n");
+ return -ENODEV;
+ }
+ normal_isa[0] = addr;
+ s_bridge = dev;
+ return i2c_add_driver(&via686a_driver);
+}
+
+static void __devexit via686a_pci_remove(struct pci_dev *dev)
+{
+ i2c_del_driver(&via686a_driver);
+}
+
+static struct pci_driver via686a_pci_driver = {
+ .name = "via686a",
+ .id_table = via686a_pci_ids,
+ .probe = via686a_pci_probe,
+ .remove = __devexit_p(via686a_pci_remove),
+};
+
+static int __init sm_via686a_init(void)
+{
+ return pci_module_init(&via686a_pci_driver);
+}
+
+static void __exit sm_via686a_exit(void)
+{
+ pci_unregister_driver(&via686a_pci_driver);
+}
+
+MODULE_AUTHOR("Kyösti Mälkki <kmalkki@cc.hut.fi>, "
+ "Mark Studebaker <mdsxyz123@yahoo.com> "
+ "and Bob Dougherty <bobd@stanford.edu>");
+MODULE_DESCRIPTION("VIA 686A Sensor device");
+MODULE_LICENSE("GPL");
+
+module_init(sm_via686a_init);
+module_exit(sm_via686a_exit);
diff --git a/drivers/i2c/chips/w83781d.c b/drivers/i2c/chips/w83781d.c
new file mode 100644
index 000000000000..af0141b87ce4
--- /dev/null
+++ b/drivers/i2c/chips/w83781d.c
@@ -0,0 +1,1882 @@
+/*
+ w83781d.c - Part of lm_sensors, Linux kernel modules for hardware
+ monitoring
+ Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>,
+ Philip Edelbrock <phil@netroedge.com>,
+ and Mark Studebaker <mdsxyz123@yahoo.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ Supports following chips:
+
+ Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA
+ as99127f 7 3 1? 3 0x30 0x12c3 yes no
+ asb100 "bach" (type_name = as99127f) 0x30 0x0694 yes no
+ w83781d 7 3 0 3 0x10 0x5ca3 yes yes
+ w83627hf 9 3 2 3 0x20 0x5ca3 yes yes(LPC)
+ w83782d 9 3 2-4 3 0x30 0x5ca3 yes yes
+ w83783s 5-6 3 2 1-2 0x40 0x5ca3 yes no
+ w83697hf 8 2 2 2 0x60 0x5ca3 no yes(LPC)
+
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+#include <linux/i2c-vid.h>
+#include <asm/io.h>
+
+/* RT Table support #defined so we can take it out if it gets bothersome */
+#define W83781D_RT 1
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { SENSORS_I2C_END };
+static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END };
+static unsigned int normal_isa[] = { 0x0290, SENSORS_ISA_END };
+static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_6(w83781d, w83782d, w83783s, w83627hf, as99127f, w83697hf);
+SENSORS_MODULE_PARM(force_subclients, "List of subclient addresses: "
+ "{bus, clientaddr, subclientaddr1, subclientaddr2}");
+
+static int init = 1;
+MODULE_PARM(init, "i");
+MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization");
+
+/* Constants specified below */
+
+/* Length of ISA address segment */
+#define W83781D_EXTENT 8
+
+/* Where are the ISA address/data registers relative to the base address */
+#define W83781D_ADDR_REG_OFFSET 5
+#define W83781D_DATA_REG_OFFSET 6
+
+/* The W83781D registers */
+/* The W83782D registers for nr=7,8 are in bank 5 */
+#define W83781D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \
+ (0x554 + (((nr) - 7) * 2)))
+#define W83781D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \
+ (0x555 + (((nr) - 7) * 2)))
+#define W83781D_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \
+ (0x550 + (nr) - 7))
+
+#define W83781D_REG_FAN_MIN(nr) (0x3a + (nr))
+#define W83781D_REG_FAN(nr) (0x27 + (nr))
+
+#define W83781D_REG_BANK 0x4E
+#define W83781D_REG_TEMP2_CONFIG 0x152
+#define W83781D_REG_TEMP3_CONFIG 0x252
+#define W83781D_REG_TEMP(nr) ((nr == 3) ? (0x0250) : \
+ ((nr == 2) ? (0x0150) : \
+ (0x27)))
+#define W83781D_REG_TEMP_HYST(nr) ((nr == 3) ? (0x253) : \
+ ((nr == 2) ? (0x153) : \
+ (0x3A)))
+#define W83781D_REG_TEMP_OVER(nr) ((nr == 3) ? (0x255) : \
+ ((nr == 2) ? (0x155) : \
+ (0x39)))
+
+#define W83781D_REG_CONFIG 0x40
+#define W83781D_REG_ALARM1 0x41
+#define W83781D_REG_ALARM2 0x42
+#define W83781D_REG_ALARM3 0x450 /* not on W83781D */
+
+#define W83781D_REG_IRQ 0x4C
+#define W83781D_REG_BEEP_CONFIG 0x4D
+#define W83781D_REG_BEEP_INTS1 0x56
+#define W83781D_REG_BEEP_INTS2 0x57
+#define W83781D_REG_BEEP_INTS3 0x453 /* not on W83781D */
+
+#define W83781D_REG_VID_FANDIV 0x47
+
+#define W83781D_REG_CHIPID 0x49
+#define W83781D_REG_WCHIPID 0x58
+#define W83781D_REG_CHIPMAN 0x4F
+#define W83781D_REG_PIN 0x4B
+
+/* 782D/783S only */
+#define W83781D_REG_VBAT 0x5D
+
+/* PWM 782D (1-4) and 783S (1-2) only */
+#define W83781D_REG_PWM1 0x5B /* 782d and 783s/627hf datasheets disagree */
+ /* on which is which; */
+#define W83781D_REG_PWM2 0x5A /* We follow the 782d convention here, */
+ /* However 782d is probably wrong. */
+#define W83781D_REG_PWM3 0x5E
+#define W83781D_REG_PWM4 0x5F
+#define W83781D_REG_PWMCLK12 0x5C
+#define W83781D_REG_PWMCLK34 0x45C
+static const u8 regpwm[] = { W83781D_REG_PWM1, W83781D_REG_PWM2,
+ W83781D_REG_PWM3, W83781D_REG_PWM4
+};
+
+#define W83781D_REG_PWM(nr) (regpwm[(nr) - 1])
+
+#define W83781D_REG_I2C_ADDR 0x48
+#define W83781D_REG_I2C_SUBADDR 0x4A
+
+/* The following are undocumented in the data sheets however we
+ received the information in an email from Winbond tech support */
+/* Sensor selection - not on 781d */
+#define W83781D_REG_SCFG1 0x5D
+static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 };
+
+#define W83781D_REG_SCFG2 0x59
+static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 };
+
+#define W83781D_DEFAULT_BETA 3435
+
+/* RT Table registers */
+#define W83781D_REG_RT_IDX 0x50
+#define W83781D_REG_RT_VAL 0x51
+
+/* Conversions. Rounding and limit checking is only done on the TO_REG
+ variants. Note that you should be a bit careful with which arguments
+ these macros are called: arguments may be evaluated more than once.
+ Fixing this is just not worth it. */
+#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255))
+#define IN_FROM_REG(val) (((val) * 16) / 10)
+
+static inline u8
+FAN_TO_REG(long rpm, int div)
+{
+ if (rpm == 0)
+ return 255;
+ rpm = SENSORS_LIMIT(rpm, 1, 1000000);
+ return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
+}
+
+#define FAN_FROM_REG(val,div) ((val) == 0 ? -1 : \
+ ((val) == 255 ? 0 : \
+ 1350000 / ((val) * (div))))
+
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val / 10) < 0 ? (((val / 10) - 5) / 10) : \
+ ((val / 10) + 5) / 10), 0, 255))
+#define TEMP_FROM_REG(val) ((((val ) > 0x80 ? (val) - 0x100 : (val)) * 10) * 10)
+
+#define TEMP_ADD_TO_REG(val) (SENSORS_LIMIT(((((val / 10) + 2) / 5) << 7),\
+ 0, 0xffff))
+#define TEMP_ADD_FROM_REG(val) ((((val) >> 7) * 5) * 10)
+
+#define AS99127_TEMP_ADD_TO_REG(val) (SENSORS_LIMIT((((((val / 10) + 2)*4)/10) \
+ << 7), 0, 0xffff))
+#define AS99127_TEMP_ADD_FROM_REG(val) (((((val) >> 7) * 10) / 4) * 10)
+
+#define ALARMS_FROM_REG(val) (val)
+#define PWM_FROM_REG(val) (val)
+#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255))
+#define BEEP_MASK_FROM_REG(val) (val)
+#define BEEP_MASK_TO_REG(val) ((val) & 0xffffff)
+
+#define BEEP_ENABLE_TO_REG(val) ((val) ? 1 : 0)
+#define BEEP_ENABLE_FROM_REG(val) ((val) ? 1 : 0)
+
+#define DIV_FROM_REG(val) (1 << (val))
+
+static inline u8
+DIV_TO_REG(long val, enum chips type)
+{
+ int i;
+ val = SENSORS_LIMIT(val, 1,
+ ((type == w83781d
+ || type == as99127f) ? 8 : 128)) >> 1;
+ for (i = 0; i < 6; i++) {
+ if (val == 0)
+ break;
+ val >>= 1;
+ }
+ return ((u8) i);
+}
+
+/* Initial limits */
+#define W83781D_INIT_IN_0 (vid == 3500 ? 280 : vid / 10)
+#define W83781D_INIT_IN_1 (vid == 3500 ? 280 : vid / 10)
+#define W83781D_INIT_IN_2 330
+#define W83781D_INIT_IN_3 (((500) * 100) / 168)
+#define W83781D_INIT_IN_4 (((1200) * 10) / 38)
+#define W83781D_INIT_IN_5 (((-1200) * -604) / 2100)
+#define W83781D_INIT_IN_6 (((-500) * -604) / 909)
+#define W83781D_INIT_IN_7 (((500) * 100) / 168)
+#define W83781D_INIT_IN_8 300
+/* Initial limits for 782d/783s negative voltages */
+/* Note level shift. Change min/max below if you change these. */
+#define W83782D_INIT_IN_5 ((((-1200) + 1491) * 100)/514)
+#define W83782D_INIT_IN_6 ((( (-500) + 771) * 100)/314)
+
+#define W83781D_INIT_IN_PERCENTAGE 10
+#define W83781D_INIT_IN_MIN(val) (val - val * W83781D_INIT_IN_PERCENTAGE / 100)
+#define W83781D_INIT_IN_MAX(val) (val + val * W83781D_INIT_IN_PERCENTAGE / 100)
+
+#define W83781D_INIT_IN_MIN_0 W83781D_INIT_IN_MIN(W83781D_INIT_IN_0)
+#define W83781D_INIT_IN_MAX_0 W83781D_INIT_IN_MAX(W83781D_INIT_IN_0)
+#define W83781D_INIT_IN_MIN_1 W83781D_INIT_IN_MIN(W83781D_INIT_IN_1)
+#define W83781D_INIT_IN_MAX_1 W83781D_INIT_IN_MAX(W83781D_INIT_IN_1)
+#define W83781D_INIT_IN_MIN_2 W83781D_INIT_IN_MIN(W83781D_INIT_IN_2)
+#define W83781D_INIT_IN_MAX_2 W83781D_INIT_IN_MAX(W83781D_INIT_IN_2)
+#define W83781D_INIT_IN_MIN_3 W83781D_INIT_IN_MIN(W83781D_INIT_IN_3)
+#define W83781D_INIT_IN_MAX_3 W83781D_INIT_IN_MAX(W83781D_INIT_IN_3)
+#define W83781D_INIT_IN_MIN_4 W83781D_INIT_IN_MIN(W83781D_INIT_IN_4)
+#define W83781D_INIT_IN_MAX_4 W83781D_INIT_IN_MAX(W83781D_INIT_IN_4)
+#define W83781D_INIT_IN_MIN_5 W83781D_INIT_IN_MIN(W83781D_INIT_IN_5)
+#define W83781D_INIT_IN_MAX_5 W83781D_INIT_IN_MAX(W83781D_INIT_IN_5)
+#define W83781D_INIT_IN_MIN_6 W83781D_INIT_IN_MIN(W83781D_INIT_IN_6)
+#define W83781D_INIT_IN_MAX_6 W83781D_INIT_IN_MAX(W83781D_INIT_IN_6)
+#define W83781D_INIT_IN_MIN_7 W83781D_INIT_IN_MIN(W83781D_INIT_IN_7)
+#define W83781D_INIT_IN_MAX_7 W83781D_INIT_IN_MAX(W83781D_INIT_IN_7)
+#define W83781D_INIT_IN_MIN_8 W83781D_INIT_IN_MIN(W83781D_INIT_IN_8)
+#define W83781D_INIT_IN_MAX_8 W83781D_INIT_IN_MAX(W83781D_INIT_IN_8)
+
+/* Initial limits for 782d/783s negative voltages */
+/* These aren't direct multiples because of level shift */
+/* Beware going negative - check */
+#define W83782D_INIT_IN_MIN_5_TMP \
+ (((-1200 * (100 + W83781D_INIT_IN_PERCENTAGE)) + (1491 * 100))/514)
+#define W83782D_INIT_IN_MIN_5 \
+ ((W83782D_INIT_IN_MIN_5_TMP > 0) ? W83782D_INIT_IN_MIN_5_TMP : 0)
+#define W83782D_INIT_IN_MAX_5 \
+ (((-1200 * (100 - W83781D_INIT_IN_PERCENTAGE)) + (1491 * 100))/514)
+#define W83782D_INIT_IN_MIN_6_TMP \
+ ((( -500 * (100 + W83781D_INIT_IN_PERCENTAGE)) + (771 * 100))/314)
+#define W83782D_INIT_IN_MIN_6 \
+ ((W83782D_INIT_IN_MIN_6_TMP > 0) ? W83782D_INIT_IN_MIN_6_TMP : 0)
+#define W83782D_INIT_IN_MAX_6 \
+ ((( -500 * (100 - W83781D_INIT_IN_PERCENTAGE)) + (771 * 100))/314)
+
+#define W83781D_INIT_FAN_MIN_1 3000
+#define W83781D_INIT_FAN_MIN_2 3000
+#define W83781D_INIT_FAN_MIN_3 3000
+
+/* temp = value / 100 */
+#define W83781D_INIT_TEMP_OVER 6000
+#define W83781D_INIT_TEMP_HYST 12700 /* must be 127 for ALARM to work */
+#define W83781D_INIT_TEMP2_OVER 6000
+#define W83781D_INIT_TEMP2_HYST 5000
+#define W83781D_INIT_TEMP3_OVER 6000
+#define W83781D_INIT_TEMP3_HYST 5000
+
+/* There are some complications in a module like this. First off, W83781D chips
+ may be both present on the SMBus and the ISA bus, and we have to handle
+ those cases separately at some places. Second, there might be several
+ W83781D chips available (well, actually, that is probably never done; but
+ it is a clean illustration of how to handle a case like that). Finally,
+ a specific chip may be attached to *both* ISA and SMBus, and we would
+ not like to detect it double. Fortunately, in the case of the W83781D at
+ least, a register tells us what SMBus address we are on, so that helps
+ a bit - except if there could be more than one SMBus. Groan. No solution
+ for this yet. */
+
+/* This module may seem overly long and complicated. In fact, it is not so
+ bad. Quite a lot of bookkeeping is done. A real driver can often cut
+ some corners. */
+
+/* For each registered W83781D, we need to keep some data in memory. That
+ data is pointed to by w83781d_list[NR]->data. The structure itself is
+ dynamically allocated, at the same time when a new w83781d client is
+ allocated. */
+struct w83781d_data {
+ struct semaphore lock;
+ enum chips type;
+
+ struct semaphore update_lock;
+ char valid; /* !=0 if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ struct i2c_client *lm75; /* for secondary I2C addresses */
+ /* pointer to array of 2 subclients */
+
+ u8 in[9]; /* Register value - 8 & 9 for 782D only */
+ u8 in_max[9]; /* Register value - 8 & 9 for 782D only */
+ u8 in_min[9]; /* Register value - 8 & 9 for 782D only */
+ u8 fan[3]; /* Register value */
+ u8 fan_min[3]; /* Register value */
+ u8 temp;
+ u8 temp_min; /* Register value */
+ u8 temp_max; /* Register value */
+ u16 temp_add[2]; /* Register value */
+ u16 temp_max_add[2]; /* Register value */
+ u16 temp_min_add[2]; /* Register value */
+ u8 fan_div[3]; /* Register encoding, shifted right */
+ u8 vid; /* Register encoding, combined */
+ u32 alarms; /* Register encoding, combined */
+ u32 beep_mask; /* Register encoding, combined */
+ u8 beep_enable; /* Boolean */
+ u8 pwm[4]; /* Register value */
+ u8 pwmenable[4]; /* Boolean */
+ u16 sens[3]; /* 782D/783S only.
+ 1 = pentium diode; 2 = 3904 diode;
+ 3000-5000 = thermistor beta.
+ Default = 3435.
+ Other Betas unimplemented */
+#ifdef W83781D_RT
+ u8 rt[3][32]; /* Register value */
+#endif
+ u8 vrm;
+};
+
+static int w83781d_attach_adapter(struct i2c_adapter *adapter);
+static int w83781d_detect(struct i2c_adapter *adapter, int address, int kind);
+static int w83781d_detach_client(struct i2c_client *client);
+
+static int w83781d_read_value(struct i2c_client *client, u16 register);
+static int w83781d_write_value(struct i2c_client *client, u16 register,
+ u16 value);
+static void w83781d_update_client(struct i2c_client *client);
+static void w83781d_init_client(struct i2c_client *client);
+
+static inline u16 swap_bytes(u16 val)
+{
+ return (val >> 8) | (val << 8);
+}
+
+static struct i2c_driver w83781d_driver = {
+ .owner = THIS_MODULE,
+ .name = "w83781d",
+ .id = I2C_DRIVERID_W83781D,
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = w83781d_attach_adapter,
+ .detach_client = w83781d_detach_client,
+};
+
+/* following are the sysfs callback functions */
+#define show_in_reg(reg) \
+static ssize_t show_##reg (struct device *dev, char *buf, int nr) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct w83781d_data *data = i2c_get_clientdata(client); \
+ \
+ w83781d_update_client(client); \
+ \
+ return sprintf(buf,"%ld\n", (long)IN_FROM_REG(data->reg[nr])); \
+}
+show_in_reg(in);
+show_in_reg(in_min);
+show_in_reg(in_max);
+
+#define store_in_reg(REG, reg) \
+static ssize_t store_in_##reg (struct device *dev, const char *buf, size_t count, int nr) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct w83781d_data *data = i2c_get_clientdata(client); \
+ u32 val; \
+ \
+ val = simple_strtoul(buf, NULL, 10); \
+ data->in_##reg[nr] = IN_TO_REG(val); \
+ w83781d_write_value(client, W83781D_REG_IN_##REG(nr), data->in_##reg[nr]); \
+ \
+ return count; \
+}
+store_in_reg(MIN, min);
+store_in_reg(MAX, max);
+
+#define sysfs_in_offset(offset) \
+static ssize_t \
+show_regs_in_##offset (struct device *dev, char *buf) \
+{ \
+ return show_in(dev, buf, 0x##offset); \
+} \
+static DEVICE_ATTR(in_input##offset, S_IRUGO, show_regs_in_##offset, NULL)
+
+#define sysfs_in_reg_offset(reg, offset) \
+static ssize_t show_regs_in_##reg##offset (struct device *dev, char *buf) \
+{ \
+ return show_in_##reg (dev, buf, 0x##offset); \
+} \
+static ssize_t store_regs_in_##reg##offset (struct device *dev, const char *buf, size_t count) \
+{ \
+ return store_in_##reg (dev, buf, count, 0x##offset); \
+} \
+static DEVICE_ATTR(in_##reg##offset, S_IRUGO| S_IWUSR, show_regs_in_##reg##offset, store_regs_in_##reg##offset)
+
+#define sysfs_in_offsets(offset) \
+sysfs_in_offset(offset); \
+sysfs_in_reg_offset(min, offset); \
+sysfs_in_reg_offset(max, offset);
+
+sysfs_in_offsets(0);
+sysfs_in_offsets(1);
+sysfs_in_offsets(2);
+sysfs_in_offsets(3);
+sysfs_in_offsets(4);
+sysfs_in_offsets(5);
+sysfs_in_offsets(6);
+sysfs_in_offsets(7);
+sysfs_in_offsets(8);
+
+#define device_create_file_in(client, offset) \
+device_create_file(&client->dev, &dev_attr_in_input##offset); \
+device_create_file(&client->dev, &dev_attr_in_min##offset); \
+device_create_file(&client->dev, &dev_attr_in_max##offset);
+
+#define show_fan_reg(reg) \
+static ssize_t show_##reg (struct device *dev, char *buf, int nr) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct w83781d_data *data = i2c_get_clientdata(client); \
+ \
+ w83781d_update_client(client); \
+ \
+ return sprintf(buf,"%ld\n", \
+ FAN_FROM_REG(data->reg[nr-1], (long)DIV_FROM_REG(data->fan_div[nr-1]))); \
+}
+show_fan_reg(fan);
+show_fan_reg(fan_min);
+
+static ssize_t
+store_fan_min(struct device *dev, const char *buf, size_t count, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83781d_data *data = i2c_get_clientdata(client);
+ u32 val;
+
+ val = simple_strtoul(buf, NULL, 10);
+ data->fan_min[nr - 1] =
+ FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr - 1]));
+ w83781d_write_value(client, W83781D_REG_FAN_MIN(nr),
+ data->fan_min[nr - 1]);
+
+ return count;
+}
+
+#define sysfs_fan_offset(offset) \
+static ssize_t show_regs_fan_##offset (struct device *dev, char *buf) \
+{ \
+ return show_fan(dev, buf, 0x##offset); \
+} \
+static DEVICE_ATTR(fan_input##offset, S_IRUGO, show_regs_fan_##offset, NULL)
+
+#define sysfs_fan_min_offset(offset) \
+static ssize_t show_regs_fan_min##offset (struct device *dev, char *buf) \
+{ \
+ return show_fan_min(dev, buf, 0x##offset); \
+} \
+static ssize_t store_regs_fan_min##offset (struct device *dev, const char *buf, size_t count) \
+{ \
+ return store_fan_min(dev, buf, count, 0x##offset); \
+} \
+static DEVICE_ATTR(fan_min##offset, S_IRUGO | S_IWUSR, show_regs_fan_min##offset, store_regs_fan_min##offset)
+
+sysfs_fan_offset(1);
+sysfs_fan_min_offset(1);
+sysfs_fan_offset(2);
+sysfs_fan_min_offset(2);
+sysfs_fan_offset(3);
+sysfs_fan_min_offset(3);
+
+#define device_create_file_fan(client, offset) \
+device_create_file(&client->dev, &dev_attr_fan_input##offset); \
+device_create_file(&client->dev, &dev_attr_fan_min##offset); \
+
+#define show_temp_reg(reg) \
+static ssize_t show_##reg (struct device *dev, char *buf, int nr) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct w83781d_data *data = i2c_get_clientdata(client); \
+ \
+ w83781d_update_client(client); \
+ \
+ if (nr >= 2) { /* TEMP2 and TEMP3 */ \
+ if (data->type == as99127f) { \
+ return sprintf(buf,"%ld\n", \
+ (long)AS99127_TEMP_ADD_FROM_REG(data->reg##_add[nr-2])); \
+ } else { \
+ return sprintf(buf,"%ld\n", \
+ (long)TEMP_ADD_FROM_REG(data->reg##_add[nr-2])); \
+ } \
+ } else { /* TEMP1 */ \
+ return sprintf(buf,"%ld\n", (long)TEMP_FROM_REG(data->reg)); \
+ } \
+}
+show_temp_reg(temp);
+show_temp_reg(temp_min);
+show_temp_reg(temp_max);
+
+#define store_temp_reg(REG, reg) \
+static ssize_t store_temp_##reg (struct device *dev, const char *buf, size_t count, int nr) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct w83781d_data *data = i2c_get_clientdata(client); \
+ u32 val; \
+ \
+ val = simple_strtoul(buf, NULL, 10); \
+ \
+ if (nr >= 2) { /* TEMP2 and TEMP3 */ \
+ if (data->type == as99127f) \
+ data->temp_##reg##_add[nr-2] = AS99127_TEMP_ADD_TO_REG(val); \
+ else \
+ data->temp_##reg##_add[nr-2] = TEMP_ADD_TO_REG(val); \
+ \
+ w83781d_write_value(client, W83781D_REG_TEMP_##REG(nr), \
+ data->temp_##reg##_add[nr-2]); \
+ } else { /* TEMP1 */ \
+ data->temp_##reg = TEMP_TO_REG(val); \
+ w83781d_write_value(client, W83781D_REG_TEMP_##REG(nr), \
+ data->temp_##reg); \
+ } \
+ \
+ return count; \
+}
+store_temp_reg(OVER, min);
+store_temp_reg(HYST, max);
+
+#define sysfs_temp_offset(offset) \
+static ssize_t \
+show_regs_temp_##offset (struct device *dev, char *buf) \
+{ \
+ return show_temp(dev, buf, 0x##offset); \
+} \
+static DEVICE_ATTR(temp_input##offset, S_IRUGO, show_regs_temp_##offset, NULL)
+
+#define sysfs_temp_reg_offset(reg, offset) \
+static ssize_t show_regs_temp_##reg##offset (struct device *dev, char *buf) \
+{ \
+ return show_temp_##reg (dev, buf, 0x##offset); \
+} \
+static ssize_t store_regs_temp_##reg##offset (struct device *dev, const char *buf, size_t count) \
+{ \
+ return store_temp_##reg (dev, buf, count, 0x##offset); \
+} \
+static DEVICE_ATTR(temp_##reg##offset, S_IRUGO| S_IWUSR, show_regs_temp_##reg##offset, store_regs_temp_##reg##offset)
+
+#define sysfs_temp_offsets(offset) \
+sysfs_temp_offset(offset); \
+sysfs_temp_reg_offset(min, offset); \
+sysfs_temp_reg_offset(max, offset);
+
+sysfs_temp_offsets(1);
+sysfs_temp_offsets(2);
+sysfs_temp_offsets(3);
+
+#define device_create_file_temp(client, offset) \
+device_create_file(&client->dev, &dev_attr_temp_input##offset); \
+device_create_file(&client->dev, &dev_attr_temp_max##offset); \
+device_create_file(&client->dev, &dev_attr_temp_min##offset);
+
+static ssize_t
+show_vid_reg(struct device *dev, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83781d_data *data = i2c_get_clientdata(client);
+
+ w83781d_update_client(client);
+
+ return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm));
+}
+
+static
+DEVICE_ATTR(vid, S_IRUGO, show_vid_reg, NULL)
+#define device_create_file_vid(client) \
+device_create_file(&client->dev, &dev_attr_vid);
+static ssize_t
+show_vrm_reg(struct device *dev, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83781d_data *data = i2c_get_clientdata(client);
+
+ w83781d_update_client(client);
+
+ return sprintf(buf, "%ld\n", (long) data->vrm);
+}
+
+static ssize_t
+store_vrm_reg(struct device *dev, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83781d_data *data = i2c_get_clientdata(client);
+ u32 val;
+
+ val = simple_strtoul(buf, NULL, 10);
+ data->vrm = val;
+
+ return count;
+}
+
+static
+DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg)
+#define device_create_file_vrm(client) \
+device_create_file(&client->dev, &dev_attr_vrm);
+static ssize_t
+show_alarms_reg(struct device *dev, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83781d_data *data = i2c_get_clientdata(client);
+
+ w83781d_update_client(client);
+
+ return sprintf(buf, "%ld\n", (long) ALARMS_FROM_REG(data->alarms));
+}
+
+static
+DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL)
+#define device_create_file_alarms(client) \
+device_create_file(&client->dev, &dev_attr_alarms);
+#define show_beep_reg(REG, reg) \
+static ssize_t show_beep_##reg (struct device *dev, char *buf) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct w83781d_data *data = i2c_get_clientdata(client); \
+ \
+ w83781d_update_client(client); \
+ \
+ return sprintf(buf,"%ld\n", (long)BEEP_##REG##_FROM_REG(data->beep_##reg)); \
+}
+show_beep_reg(ENABLE, enable);
+show_beep_reg(MASK, mask);
+
+#define BEEP_ENABLE 0 /* Store beep_enable */
+#define BEEP_MASK 1 /* Store beep_mask */
+
+static ssize_t
+store_beep_reg(struct device *dev, const char *buf, size_t count,
+ int update_mask)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83781d_data *data = i2c_get_clientdata(client);
+ u32 val, val2;
+
+ val = simple_strtoul(buf, NULL, 10);
+
+ if (update_mask == BEEP_MASK) { /* We are storing beep_mask */
+ data->beep_mask = BEEP_MASK_TO_REG(val);
+ w83781d_write_value(client, W83781D_REG_BEEP_INTS1,
+ data->beep_mask & 0xff);
+
+ if ((data->type != w83781d) && (data->type != as99127f)) {
+ w83781d_write_value(client, W83781D_REG_BEEP_INTS3,
+ ((data->beep_mask) >> 16) & 0xff);
+ }
+
+ val2 = (data->beep_mask >> 8) & 0x7f;
+ } else { /* We are storing beep_enable */
+ val2 = w83781d_read_value(client, W83781D_REG_BEEP_INTS2) & 0x7f;
+ data->beep_enable = BEEP_ENABLE_TO_REG(val);
+ }
+
+ w83781d_write_value(client, W83781D_REG_BEEP_INTS2,
+ val2 | data->beep_enable << 7);
+
+ return count;
+}
+
+#define sysfs_beep(REG, reg) \
+static ssize_t show_regs_beep_##reg (struct device *dev, char *buf) \
+{ \
+ return show_beep_##reg(dev, buf); \
+} \
+static ssize_t store_regs_beep_##reg (struct device *dev, const char *buf, size_t count) \
+{ \
+ return store_beep_reg(dev, buf, count, BEEP_##REG); \
+} \
+static DEVICE_ATTR(beep_##reg, S_IRUGO | S_IWUSR, show_regs_beep_##reg, store_regs_beep_##reg)
+
+sysfs_beep(ENABLE, enable);
+sysfs_beep(MASK, mask);
+
+#define device_create_file_beep(client) \
+device_create_file(&client->dev, &dev_attr_beep_enable); \
+device_create_file(&client->dev, &dev_attr_beep_mask);
+
+/* w83697hf only has two fans */
+static ssize_t
+show_fan_div_reg(struct device *dev, char *buf, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83781d_data *data = i2c_get_clientdata(client);
+
+ w83781d_update_client(client);
+
+ return sprintf(buf, "%ld\n",
+ (long) DIV_FROM_REG(data->fan_div[nr - 1]));
+}
+
+/* w83697hf only has two fans */
+static ssize_t
+store_fan_div_reg(struct device *dev, const char *buf, size_t count, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83781d_data *data = i2c_get_clientdata(client);
+ u32 val, old, old2, old3;
+
+ val = simple_strtoul(buf, NULL, 10);
+ old = w83781d_read_value(client, W83781D_REG_VID_FANDIV);
+
+ data->fan_div[nr - 1] = DIV_TO_REG(val, data->type);
+
+ /* w83781d and as99127f don't have extended divisor bits */
+ if ((data->type != w83781d) && data->type != as99127f) {
+ old3 = w83781d_read_value(client, W83781D_REG_VBAT);
+ }
+ if (nr >= 3 && data->type != w83697hf) {
+ old2 = w83781d_read_value(client, W83781D_REG_PIN);
+ old2 = (old2 & 0x3f) | ((data->fan_div[2] & 0x03) << 6);
+ w83781d_write_value(client, W83781D_REG_PIN, old2);
+
+ if ((data->type != w83781d) && (data->type != as99127f)) {
+ old3 = (old3 & 0x7f) | ((data->fan_div[2] & 0x04) << 5);
+ }
+ }
+ if (nr >= 2) {
+ old = (old & 0x3f) | ((data->fan_div[1] & 0x03) << 6);
+
+ if ((data->type != w83781d) && (data->type != as99127f)) {
+ old3 = (old3 & 0xbf) | ((data->fan_div[1] & 0x04) << 4);
+ }
+ }
+ if (nr >= 1) {
+ old = (old & 0xcf) | ((data->fan_div[0] & 0x03) << 4);
+ w83781d_write_value(client, W83781D_REG_VID_FANDIV, old);
+
+ if ((data->type != w83781d) && (data->type != as99127f)) {
+ old3 = (old3 & 0xdf) | ((data->fan_div[0] & 0x04) << 3);
+ w83781d_write_value(client, W83781D_REG_VBAT, old3);
+ }
+ }
+
+ return count;
+}
+
+#define sysfs_fan_div(offset) \
+static ssize_t show_regs_fan_div_##offset (struct device *dev, char *buf) \
+{ \
+ return show_fan_div_reg(dev, buf, offset); \
+} \
+static ssize_t store_regs_fan_div_##offset (struct device *dev, const char *buf, size_t count) \
+{ \
+ return store_fan_div_reg(dev, buf, count, offset); \
+} \
+static DEVICE_ATTR(fan_div##offset, S_IRUGO | S_IWUSR, show_regs_fan_div_##offset, store_regs_fan_div_##offset)
+
+sysfs_fan_div(1);
+sysfs_fan_div(2);
+sysfs_fan_div(3);
+
+#define device_create_file_fan_div(client, offset) \
+device_create_file(&client->dev, &dev_attr_fan_div##offset); \
+
+/* w83697hf only has two fans */
+static ssize_t
+show_pwm_reg(struct device *dev, char *buf, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83781d_data *data = i2c_get_clientdata(client);
+
+ w83781d_update_client(client);
+
+ return sprintf(buf, "%ld\n", (long) PWM_FROM_REG(data->pwm[nr - 1]));
+}
+
+/* w83697hf only has two fans */
+static ssize_t
+show_pwmenable_reg(struct device *dev, char *buf, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83781d_data *data = i2c_get_clientdata(client);
+
+ w83781d_update_client(client);
+
+ return sprintf(buf, "%ld\n", (long) data->pwmenable[nr - 1]);
+}
+
+static ssize_t
+store_pwm_reg(struct device *dev, const char *buf, size_t count, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83781d_data *data = i2c_get_clientdata(client);
+ u32 val;
+
+ val = simple_strtoul(buf, NULL, 10);
+
+ data->pwm[nr - 1] = PWM_TO_REG(val);
+ w83781d_write_value(client, W83781D_REG_PWM(nr), data->pwm[nr - 1]);
+
+ return count;
+}
+
+static ssize_t
+store_pwmenable_reg(struct device *dev, const char *buf, size_t count, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83781d_data *data = i2c_get_clientdata(client);
+ u32 val, j, k;
+
+ val = simple_strtoul(buf, NULL, 10);
+
+ /* only PWM2 can be enabled/disabled */
+ if (nr == 2) {
+ j = w83781d_read_value(client, W83781D_REG_PWMCLK12);
+ k = w83781d_read_value(client, W83781D_REG_BEEP_CONFIG);
+
+ if (val > 0) {
+ if (!(j & 0x08))
+ w83781d_write_value(client,
+ W83781D_REG_PWMCLK12,
+ j | 0x08);
+ if (k & 0x10)
+ w83781d_write_value(client,
+ W83781D_REG_BEEP_CONFIG,
+ k & 0xef);
+
+ data->pwmenable[1] = 1;
+ } else {
+ if (j & 0x08)
+ w83781d_write_value(client,
+ W83781D_REG_PWMCLK12,
+ j & 0xf7);
+ if (!(k & 0x10))
+ w83781d_write_value(client,
+ W83781D_REG_BEEP_CONFIG,
+ j | 0x10);
+
+ data->pwmenable[1] = 0;
+ }
+ }
+
+ return count;
+}
+
+#define sysfs_pwm(offset) \
+static ssize_t show_regs_pwm_##offset (struct device *dev, char *buf) \
+{ \
+ return show_pwm_reg(dev, buf, offset); \
+} \
+static ssize_t store_regs_pwm_##offset (struct device *dev, const char *buf, size_t count) \
+{ \
+ return store_pwm_reg(dev, buf, count, offset); \
+} \
+static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, show_regs_pwm_##offset, store_regs_pwm_##offset)
+
+#define sysfs_pwmenable(offset) \
+static ssize_t show_regs_pwmenable_##offset (struct device *dev, char *buf) \
+{ \
+ return show_pwmenable_reg(dev, buf, offset); \
+} \
+static ssize_t store_regs_pwmenable_##offset (struct device *dev, const char *buf, size_t count) \
+{ \
+ return store_pwmenable_reg(dev, buf, count, offset); \
+} \
+static DEVICE_ATTR(pwm_enable##offset, S_IRUGO | S_IWUSR, show_regs_pwmenable_##offset, store_regs_pwmenable_##offset)
+
+sysfs_pwm(1);
+sysfs_pwm(2);
+sysfs_pwmenable(2); /* only PWM2 can be enabled/disabled */
+sysfs_pwm(3);
+sysfs_pwm(4);
+
+#define device_create_file_pwm(client, offset) \
+device_create_file(&client->dev, &dev_attr_pwm##offset); \
+
+#define device_create_file_pwmenable(client, offset) \
+device_create_file(&client->dev, &dev_attr_pwm_enable##offset); \
+
+static ssize_t
+show_sensor_reg(struct device *dev, char *buf, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83781d_data *data = i2c_get_clientdata(client);
+
+ w83781d_update_client(client);
+
+ return sprintf(buf, "%ld\n", (long) data->sens[nr - 1]);
+}
+
+static ssize_t
+store_sensor_reg(struct device *dev, const char *buf, size_t count, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83781d_data *data = i2c_get_clientdata(client);
+ u32 val, tmp;
+
+ val = simple_strtoul(buf, NULL, 10);
+
+ switch (val) {
+ case 1: /* PII/Celeron diode */
+ tmp = w83781d_read_value(client, W83781D_REG_SCFG1);
+ w83781d_write_value(client, W83781D_REG_SCFG1,
+ tmp | BIT_SCFG1[nr - 1]);
+ tmp = w83781d_read_value(client, W83781D_REG_SCFG2);
+ w83781d_write_value(client, W83781D_REG_SCFG2,
+ tmp | BIT_SCFG2[nr - 1]);
+ data->sens[nr - 1] = val;
+ break;
+ case 2: /* 3904 */
+ tmp = w83781d_read_value(client, W83781D_REG_SCFG1);
+ w83781d_write_value(client, W83781D_REG_SCFG1,
+ tmp | BIT_SCFG1[nr - 1]);
+ tmp = w83781d_read_value(client, W83781D_REG_SCFG2);
+ w83781d_write_value(client, W83781D_REG_SCFG2,
+ tmp & ~BIT_SCFG2[nr - 1]);
+ data->sens[nr - 1] = val;
+ break;
+ case W83781D_DEFAULT_BETA: /* thermistor */
+ tmp = w83781d_read_value(client, W83781D_REG_SCFG1);
+ w83781d_write_value(client, W83781D_REG_SCFG1,
+ tmp & ~BIT_SCFG1[nr - 1]);
+ data->sens[nr - 1] = val;
+ break;
+ default:
+ dev_err(&client->dev,
+ "Invalid sensor type %ld; must be 1, 2, or %d\n",
+ (long) val, W83781D_DEFAULT_BETA);
+ break;
+ }
+
+ return count;
+}
+
+#define sysfs_sensor(offset) \
+static ssize_t show_regs_sensor_##offset (struct device *dev, char *buf) \
+{ \
+ return show_sensor_reg(dev, buf, offset); \
+} \
+static ssize_t store_regs_sensor_##offset (struct device *dev, const char *buf, size_t count) \
+{ \
+ return store_sensor_reg(dev, buf, count, offset); \
+} \
+static DEVICE_ATTR(sensor##offset, S_IRUGO | S_IWUSR, show_regs_sensor_##offset, store_regs_sensor_##offset)
+
+sysfs_sensor(1);
+sysfs_sensor(2);
+sysfs_sensor(3);
+
+#define device_create_file_sensor(client, offset) \
+device_create_file(&client->dev, &dev_attr_sensor##offset); \
+
+#ifdef W83781D_RT
+static ssize_t
+show_rt_reg(struct device *dev, char *buf, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83781d_data *data = i2c_get_clientdata(client);
+ int i, j = 0;
+
+ w83781d_update_client(client);
+
+ for (i = 0; i < 32; i++) {
+ if (i > 0)
+ j += sprintf(buf, " %ld", (long) data->rt[nr - 1][i]);
+ else
+ j += sprintf(buf, "%ld", (long) data->rt[nr - 1][i]);
+ }
+ j += sprintf(buf, "\n");
+
+ return j;
+}
+
+static ssize_t
+store_rt_reg(struct device *dev, const char *buf, size_t count, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83781d_data *data = i2c_get_clientdata(client);
+ u32 val, i;
+
+ for (i = 0; i < count; i++) {
+ val = simple_strtoul(buf + count, NULL, 10);
+
+ /* fixme: no bounds checking 0-255 */
+ data->rt[nr - 1][i] = val & 0xff;
+ w83781d_write_value(client, W83781D_REG_RT_IDX, i);
+ w83781d_write_value(client, W83781D_REG_RT_VAL,
+ data->rt[nr - 1][i]);
+ }
+
+ return count;
+}
+
+#define sysfs_rt(offset) \
+static ssize_t show_regs_rt_##offset (struct device *dev, char *buf) \
+{ \
+ return show_rt_reg(dev, buf, offset); \
+} \
+static ssize_t store_regs_rt_##offset (struct device *dev, const char *buf, size_t count) \
+{ \
+ return store_rt_reg(dev, buf, count, offset); \
+} \
+static DEVICE_ATTR(rt##offset, S_IRUGO | S_IWUSR, show_regs_rt_##offset, store_regs_rt_##offset)
+
+sysfs_rt(1);
+sysfs_rt(2);
+sysfs_rt(3);
+
+#define device_create_file_rt(client, offset) \
+device_create_file(&client->dev, &dev_attr_rt##offset); \
+
+#endif /* ifdef W83781D_RT */
+
+/* This function is called when:
+ * w83781d_driver is inserted (when this module is loaded), for each
+ available adapter
+ * when a new adapter is inserted (and w83781d_driver is still present) */
+static int
+w83781d_attach_adapter(struct i2c_adapter *adapter)
+{
+ return i2c_detect(adapter, &addr_data, w83781d_detect);
+}
+
+static int
+w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+ int i = 0, val1 = 0, val2, id;
+ struct i2c_client *new_client;
+ struct w83781d_data *data;
+ int err = 0;
+ const char *type_name = "";
+ const char *client_name = "";
+ int is_isa = i2c_is_isa_adapter(adapter);
+ enum vendor { winbond, asus } vendid;
+
+ if (!is_isa
+ && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ goto ERROR0;
+
+ if (is_isa) {
+ if (!request_region(address, W83781D_EXTENT, "w83781d"))
+ goto ERROR0;
+ release_region(address, W83781D_EXTENT);
+ }
+
+ /* Probe whether there is anything available on this address. Already
+ done for SMBus clients */
+ if (kind < 0) {
+ if (is_isa) {
+
+#define REALLY_SLOW_IO
+ /* We need the timeouts for at least some LM78-like chips. But only
+ if we read 'undefined' registers. */
+ i = inb_p(address + 1);
+ if (inb_p(address + 2) != i)
+ goto ERROR0;
+ if (inb_p(address + 3) != i)
+ goto ERROR0;
+ if (inb_p(address + 7) != i)
+ goto ERROR0;
+#undef REALLY_SLOW_IO
+
+ /* Let's just hope nothing breaks here */
+ i = inb_p(address + 5) & 0x7f;
+ outb_p(~i & 0x7f, address + 5);
+ if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) {
+ outb_p(i, address + 5);
+ return 0;
+ }
+ }
+ }
+
+ /* OK. For now, we presume we have a valid client. We now create the
+ client structure, even though we cannot fill it completely yet.
+ But it allows us to access w83781d_{read,write}_value. */
+
+ if (!(new_client = kmalloc(sizeof (struct i2c_client) +
+ sizeof (struct w83781d_data), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto ERROR0;
+ }
+
+ memset(new_client, 0x00, sizeof (struct i2c_client) +
+ sizeof (struct w83781d_data));
+
+ data = (struct w83781d_data *) (new_client + 1);
+ i2c_set_clientdata(new_client, data);
+ new_client->addr = address;
+ init_MUTEX(&data->lock);
+ new_client->adapter = adapter;
+ new_client->driver = &w83781d_driver;
+ new_client->flags = 0;
+
+ /* Now, we do the remaining detection. */
+
+ /* The w8378?d may be stuck in some other bank than bank 0. This may
+ make reading other information impossible. Specify a force=... or
+ force_*=... parameter, and the Winbond will be reset to the right
+ bank. */
+ if (kind < 0) {
+ if (w83781d_read_value(new_client, W83781D_REG_CONFIG) & 0x80)
+ goto ERROR1;
+ val1 = w83781d_read_value(new_client, W83781D_REG_BANK);
+ val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN);
+ /* Check for Winbond or Asus ID if in bank 0 */
+ if ((!(val1 & 0x07)) &&
+ (((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3)
+ && (val2 != 0x94))
+ || ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12)
+ && (val2 != 0x06))))
+ goto ERROR1;
+ /* If Winbond SMBus, check address at 0x48. Asus doesn't support */
+ if ((!is_isa) && (((!(val1 & 0x80)) && (val2 == 0xa3)) ||
+ ((val1 & 0x80) && (val2 == 0x5c)))) {
+ if (w83781d_read_value
+ (new_client, W83781D_REG_I2C_ADDR) != address)
+ goto ERROR1;
+ }
+ }
+
+ /* We have either had a force parameter, or we have already detected the
+ Winbond. Put it now into bank 0 and Vendor ID High Byte */
+ w83781d_write_value(new_client, W83781D_REG_BANK,
+ (w83781d_read_value(new_client,
+ W83781D_REG_BANK) & 0x78) |
+ 0x80);
+
+ /* Determine the chip type. */
+ if (kind <= 0) {
+ /* get vendor ID */
+ val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN);
+ if (val2 == 0x5c)
+ vendid = winbond;
+ else if ((val2 == 0x12) || (val2 == 0x06))
+ vendid = asus;
+ else
+ goto ERROR1;
+ /* mask off lower bit, not reliable */
+ val1 =
+ w83781d_read_value(new_client, W83781D_REG_WCHIPID) & 0xfe;
+ if (val1 == 0x10 && vendid == winbond)
+ kind = w83781d;
+ else if (val1 == 0x30 && vendid == winbond)
+ kind = w83782d;
+ else if (val1 == 0x40 && vendid == winbond && !is_isa)
+ kind = w83783s;
+ else if (val1 == 0x20 && vendid == winbond)
+ kind = w83627hf;
+ else if (val1 == 0x30 && vendid == asus && !is_isa)
+ kind = as99127f;
+ else if (val1 == 0x60 && vendid == winbond && is_isa)
+ kind = w83697hf;
+ else {
+ if (kind == 0)
+ dev_warn(&new_client->dev,
+ "Ignoring 'force' parameter for unknown chip at"
+ "adapter %d, address 0x%02x\n",
+ i2c_adapter_id(adapter), address);
+ goto ERROR1;
+ }
+ }
+
+ if (kind == w83781d) {
+ type_name = "w83781d";
+ client_name = "W83781D chip";
+ } else if (kind == w83782d) {
+ type_name = "w83782d";
+ client_name = "W83782D chip";
+ } else if (kind == w83783s) {
+ type_name = "w83783s";
+ client_name = "W83783S chip";
+ } else if (kind == w83627hf) {
+ type_name = "w83627hf";
+ client_name = "W83627HF chip";
+ } else if (kind == as99127f) {
+ type_name = "as99127f";
+ client_name = "AS99127F chip";
+ } else if (kind == w83697hf) {
+ type_name = "w83697hf";
+ client_name = "W83697HF chip";
+ } else {
+ dev_err(&new_client->dev, "Internal error: unknown kind (%d)?!?", kind);
+ goto ERROR1;
+ }
+
+ /* Reserve the ISA region */
+ if (is_isa)
+ request_region(address, W83781D_EXTENT, type_name);
+
+ /* Fill in the remaining client fields and put it into the global list */
+ strncpy(new_client->dev.name, client_name, DEVICE_NAME_SIZE);
+ data->type = kind;
+
+ data->valid = 0;
+ init_MUTEX(&data->update_lock);
+
+ /* Tell the I2C layer a new client has arrived */
+ if ((err = i2c_attach_client(new_client)))
+ goto ERROR3;
+
+ /* attach secondary i2c lm75-like clients */
+ if (!is_isa) {
+ if (!(data->lm75 = kmalloc(2 * sizeof (struct i2c_client),
+ GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto ERROR4;
+ }
+
+ memset(data->lm75, 0x00, 2 * sizeof (struct i2c_client));
+
+ id = i2c_adapter_id(adapter);
+ if (force_subclients[0] == id && force_subclients[1] == address) {
+ for (i = 2; i <= 3; i++) {
+ if (force_subclients[i] < 0x48 ||
+ force_subclients[i] > 0x4f) {
+ dev_err(&new_client->dev,
+ "Invalid subclient address %d; must be 0x48-0x4f\n",
+ force_subclients[i]);
+ goto ERROR5;
+ }
+ }
+ w83781d_write_value(new_client,
+ W83781D_REG_I2C_SUBADDR,
+ (force_subclients[2] & 0x07) |
+ ((force_subclients[3] & 0x07) <<
+ 4));
+ data->lm75[0].addr = force_subclients[2];
+ } else {
+ val1 = w83781d_read_value(new_client,
+ W83781D_REG_I2C_SUBADDR);
+ data->lm75[0].addr = 0x48 + (val1 & 0x07);
+ }
+ if (kind != w83783s) {
+ if (force_subclients[0] == id &&
+ force_subclients[1] == address) {
+ data->lm75[1].addr = force_subclients[3];
+ } else {
+ data->lm75[1].addr =
+ 0x48 + ((val1 >> 4) & 0x07);
+ }
+ if (data->lm75[0].addr == data->lm75[1].addr) {
+ dev_err(&new_client->dev,
+ "Duplicate addresses 0x%x for subclients.\n",
+ data->lm75[0].addr);
+ goto ERROR5;
+ }
+ }
+ if (kind == w83781d)
+ client_name = "W83781D subclient";
+ else if (kind == w83782d)
+ client_name = "W83782D subclient";
+ else if (kind == w83783s)
+ client_name = "W83783S subclient";
+ else if (kind == w83627hf)
+ client_name = "W83627HF subclient";
+ else if (kind == as99127f)
+ client_name = "AS99127F subclient";
+
+ for (i = 0; i <= 1; i++) {
+ i2c_set_clientdata(&data->lm75[i], NULL); /* store all data in w83781d */
+ data->lm75[i].adapter = adapter;
+ data->lm75[i].driver = &w83781d_driver;
+ data->lm75[i].flags = 0;
+ strncpy(data->lm75[i].dev.name, client_name,
+ DEVICE_NAME_SIZE);
+ if (kind == w83783s)
+ break;
+ }
+ } else {
+ data->lm75 = NULL;
+ }
+
+ device_create_file_in(new_client, 0);
+ if (kind != w83783s && kind != w83697hf)
+ device_create_file_in(new_client, 1);
+ device_create_file_in(new_client, 2);
+ device_create_file_in(new_client, 3);
+ device_create_file_in(new_client, 4);
+ device_create_file_in(new_client, 5);
+ device_create_file_in(new_client, 6);
+ if (kind != as99127f && kind != w83781d && kind != w83783s) {
+ device_create_file_in(new_client, 7);
+ device_create_file_in(new_client, 8);
+ }
+
+ device_create_file_fan(new_client, 1);
+ device_create_file_fan(new_client, 2);
+ if (kind != w83697hf)
+ device_create_file_fan(new_client, 3);
+
+ device_create_file_temp(new_client, 1);
+ device_create_file_temp(new_client, 2);
+ if (kind != w83783s && kind != w83697hf)
+ device_create_file_temp(new_client, 3);
+
+ if (kind != w83697hf)
+ device_create_file_vid(new_client);
+
+ if (kind != w83697hf)
+ device_create_file_vrm(new_client);
+
+ device_create_file_fan_div(new_client, 1);
+ device_create_file_fan_div(new_client, 2);
+ if (kind != w83697hf)
+ device_create_file_fan_div(new_client, 3);
+
+ device_create_file_alarms(new_client);
+
+ device_create_file_beep(new_client);
+
+ if (kind != w83781d) {
+ device_create_file_pwm(new_client, 1);
+ device_create_file_pwm(new_client, 2);
+ device_create_file_pwmenable(new_client, 2);
+ }
+ if (kind == w83782d && !is_isa) {
+ device_create_file_pwm(new_client, 3);
+ device_create_file_pwm(new_client, 4);
+ }
+
+ if (kind != as99127f && kind != w83781d) {
+ device_create_file_sensor(new_client, 1);
+ device_create_file_sensor(new_client, 2);
+ if (kind != w83783s && kind != w83697hf)
+ device_create_file_sensor(new_client, 3);
+ }
+#ifdef W83781D_RT
+ if (kind == w83781d) {
+ device_create_file_rt(new_client, 1);
+ device_create_file_rt(new_client, 2);
+ device_create_file_rt(new_client, 3);
+ }
+#endif
+
+ /* Initialize the chip */
+ w83781d_init_client(new_client);
+ return 0;
+
+/* OK, this is not exactly good programming practice, usually. But it is
+ very code-efficient in this case. */
+
+ ERROR5:
+ if (!is_isa) {
+ i2c_detach_client(&data->lm75[0]);
+ if (data->type != w83783s)
+ i2c_detach_client(&data->lm75[1]);
+ kfree(data->lm75);
+ }
+ ERROR4:
+ i2c_detach_client(new_client);
+ ERROR3:
+ if (is_isa)
+ release_region(address, W83781D_EXTENT);
+ ERROR1:
+ kfree(new_client);
+ ERROR0:
+ return err;
+}
+
+static int
+w83781d_detach_client(struct i2c_client *client)
+{
+ struct w83781d_data *data = i2c_get_clientdata(client);
+ int err;
+
+ if ((err = i2c_detach_client(client))) {
+ dev_err(&client->dev,
+ "Client deregistration failed, client not detached.\n");
+ return err;
+ }
+
+ if (i2c_is_isa_client(client)) {
+ release_region(client->addr, W83781D_EXTENT);
+ } else {
+ i2c_detach_client(&data->lm75[0]);
+ if (data->type != w83783s)
+ i2c_detach_client(&data->lm75[1]);
+ kfree(data->lm75);
+ }
+ kfree(client);
+
+ return 0;
+}
+
+/* The SMBus locks itself, usually, but nothing may access the Winbond between
+ bank switches. ISA access must always be locked explicitly!
+ We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks,
+ would slow down the W83781D access and should not be necessary.
+ There are some ugly typecasts here, but the good news is - they should
+ nowhere else be necessary! */
+static int
+w83781d_read_value(struct i2c_client *client, u16 reg)
+{
+ struct w83781d_data *data = i2c_get_clientdata(client);
+ int res, word_sized, bank;
+ struct i2c_client *cl;
+
+ down(&data->lock);
+ if (i2c_is_isa_client(client)) {
+ word_sized = (((reg & 0xff00) == 0x100)
+ || ((reg & 0xff00) == 0x200))
+ && (((reg & 0x00ff) == 0x50)
+ || ((reg & 0x00ff) == 0x53)
+ || ((reg & 0x00ff) == 0x55));
+ if (reg & 0xff00) {
+ outb_p(W83781D_REG_BANK,
+ client->addr + W83781D_ADDR_REG_OFFSET);
+ outb_p(reg >> 8,
+ client->addr + W83781D_DATA_REG_OFFSET);
+ }
+ outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET);
+ res = inb_p(client->addr + W83781D_DATA_REG_OFFSET);
+ if (word_sized) {
+ outb_p((reg & 0xff) + 1,
+ client->addr + W83781D_ADDR_REG_OFFSET);
+ res =
+ (res << 8) + inb_p(client->addr +
+ W83781D_DATA_REG_OFFSET);
+ }
+ if (reg & 0xff00) {
+ outb_p(W83781D_REG_BANK,
+ client->addr + W83781D_ADDR_REG_OFFSET);
+ outb_p(0, client->addr + W83781D_DATA_REG_OFFSET);
+ }
+ } else {
+ bank = (reg >> 8) & 0x0f;
+ if (bank > 2)
+ /* switch banks */
+ i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
+ bank);
+ if (bank == 0 || bank > 2) {
+ res = i2c_smbus_read_byte_data(client, reg & 0xff);
+ } else {
+ /* switch to subclient */
+ cl = &data->lm75[bank - 1];
+ /* convert from ISA to LM75 I2C addresses */
+ switch (reg & 0xff) {
+ case 0x50: /* TEMP */
+ res =
+ swap_bytes(i2c_smbus_read_word_data(cl, 0));
+ break;
+ case 0x52: /* CONFIG */
+ res = i2c_smbus_read_byte_data(cl, 1);
+ break;
+ case 0x53: /* HYST */
+ res =
+ swap_bytes(i2c_smbus_read_word_data(cl, 2));
+ break;
+ case 0x55: /* OVER */
+ default:
+ res =
+ swap_bytes(i2c_smbus_read_word_data(cl, 3));
+ break;
+ }
+ }
+ if (bank > 2)
+ i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0);
+ }
+ up(&data->lock);
+ return res;
+}
+
+static int
+w83781d_write_value(struct i2c_client *client, u16 reg, u16 value)
+{
+ struct w83781d_data *data = i2c_get_clientdata(client);
+ int word_sized, bank;
+ struct i2c_client *cl;
+
+ down(&data->lock);
+ if (i2c_is_isa_client(client)) {
+ word_sized = (((reg & 0xff00) == 0x100)
+ || ((reg & 0xff00) == 0x200))
+ && (((reg & 0x00ff) == 0x53)
+ || ((reg & 0x00ff) == 0x55));
+ if (reg & 0xff00) {
+ outb_p(W83781D_REG_BANK,
+ client->addr + W83781D_ADDR_REG_OFFSET);
+ outb_p(reg >> 8,
+ client->addr + W83781D_DATA_REG_OFFSET);
+ }
+ outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET);
+ if (word_sized) {
+ outb_p(value >> 8,
+ client->addr + W83781D_DATA_REG_OFFSET);
+ outb_p((reg & 0xff) + 1,
+ client->addr + W83781D_ADDR_REG_OFFSET);
+ }
+ outb_p(value & 0xff, client->addr + W83781D_DATA_REG_OFFSET);
+ if (reg & 0xff00) {
+ outb_p(W83781D_REG_BANK,
+ client->addr + W83781D_ADDR_REG_OFFSET);
+ outb_p(0, client->addr + W83781D_DATA_REG_OFFSET);
+ }
+ } else {
+ bank = (reg >> 8) & 0x0f;
+ if (bank > 2)
+ /* switch banks */
+ i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
+ bank);
+ if (bank == 0 || bank > 2) {
+ i2c_smbus_write_byte_data(client, reg & 0xff,
+ value & 0xff);
+ } else {
+ /* switch to subclient */
+ cl = &data->lm75[bank - 1];
+ /* convert from ISA to LM75 I2C addresses */
+ switch (reg & 0xff) {
+ case 0x52: /* CONFIG */
+ i2c_smbus_write_byte_data(cl, 1, value & 0xff);
+ break;
+ case 0x53: /* HYST */
+ i2c_smbus_write_word_data(cl, 2,
+ swap_bytes(value));
+ break;
+ case 0x55: /* OVER */
+ i2c_smbus_write_word_data(cl, 3,
+ swap_bytes(value));
+ break;
+ }
+ }
+ if (bank > 2)
+ i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0);
+ }
+ up(&data->lock);
+ return 0;
+}
+
+/* Called when we have found a new W83781D. It should set limits, etc. */
+static void
+w83781d_init_client(struct i2c_client *client)
+{
+ struct w83781d_data *data = i2c_get_clientdata(client);
+ int vid = 0, i, p;
+ int type = data->type;
+ u8 tmp;
+
+ if (init && type != as99127f) { /* this resets registers we don't have
+ documentation for on the as99127f */
+ /* save these registers */
+ i = w83781d_read_value(client, W83781D_REG_BEEP_CONFIG);
+ p = w83781d_read_value(client, W83781D_REG_PWMCLK12);
+ /* Reset all except Watchdog values and last conversion values
+ This sets fan-divs to 2, among others */
+ w83781d_write_value(client, W83781D_REG_CONFIG, 0x80);
+ /* Restore the registers and disable power-on abnormal beep.
+ This saves FAN 1/2/3 input/output values set by BIOS. */
+ w83781d_write_value(client, W83781D_REG_BEEP_CONFIG, i | 0x80);
+ w83781d_write_value(client, W83781D_REG_PWMCLK12, p);
+ /* Disable master beep-enable (reset turns it on).
+ Individual beep_mask should be reset to off but for some reason
+ disabling this bit helps some people not get beeped */
+ w83781d_write_value(client, W83781D_REG_BEEP_INTS2, 0);
+ }
+
+ if (type != w83697hf) {
+ vid = w83781d_read_value(client, W83781D_REG_VID_FANDIV) & 0x0f;
+ vid |=
+ (w83781d_read_value(client, W83781D_REG_CHIPID) & 0x01) <<
+ 4;
+ data->vrm = DEFAULT_VRM;
+ vid = vid_from_reg(vid, data->vrm);
+ }
+
+ if ((type != w83781d) && (type != as99127f)) {
+ tmp = w83781d_read_value(client, W83781D_REG_SCFG1);
+ for (i = 1; i <= 3; i++) {
+ if (!(tmp & BIT_SCFG1[i - 1])) {
+ data->sens[i - 1] = W83781D_DEFAULT_BETA;
+ } else {
+ if (w83781d_read_value
+ (client,
+ W83781D_REG_SCFG2) & BIT_SCFG2[i - 1])
+ data->sens[i - 1] = 1;
+ else
+ data->sens[i - 1] = 2;
+ }
+ if ((type == w83783s || type == w83697hf) && (i == 2))
+ break;
+ }
+ }
+#ifdef W83781D_RT
+/*
+ Fill up the RT Tables.
+ We assume that they are 32 bytes long, in order for temp 1-3.
+ Data sheet documentation is sparse.
+ We also assume that it is only for the 781D although I suspect
+ that the others support it as well....
+*/
+
+ if (init && type == w83781d) {
+ u16 k = 0;
+/*
+ Auto-indexing doesn't seem to work...
+ w83781d_write_value(client,W83781D_REG_RT_IDX,0);
+*/
+ for (i = 0; i < 3; i++) {
+ int j;
+ for (j = 0; j < 32; j++) {
+ w83781d_write_value(client,
+ W83781D_REG_RT_IDX, k++);
+ data->rt[i][j] =
+ w83781d_read_value(client,
+ W83781D_REG_RT_VAL);
+ }
+ }
+ }
+#endif /* W83781D_RT */
+
+ if (init) {
+ w83781d_write_value(client, W83781D_REG_IN_MIN(0),
+ IN_TO_REG(W83781D_INIT_IN_MIN_0));
+ w83781d_write_value(client, W83781D_REG_IN_MAX(0),
+ IN_TO_REG(W83781D_INIT_IN_MAX_0));
+ if (type != w83783s && type != w83697hf) {
+ w83781d_write_value(client, W83781D_REG_IN_MIN(1),
+ IN_TO_REG(W83781D_INIT_IN_MIN_1));
+ w83781d_write_value(client, W83781D_REG_IN_MAX(1),
+ IN_TO_REG(W83781D_INIT_IN_MAX_1));
+ }
+
+ w83781d_write_value(client, W83781D_REG_IN_MIN(2),
+ IN_TO_REG(W83781D_INIT_IN_MIN_2));
+ w83781d_write_value(client, W83781D_REG_IN_MAX(2),
+ IN_TO_REG(W83781D_INIT_IN_MAX_2));
+ w83781d_write_value(client, W83781D_REG_IN_MIN(3),
+ IN_TO_REG(W83781D_INIT_IN_MIN_3));
+ w83781d_write_value(client, W83781D_REG_IN_MAX(3),
+ IN_TO_REG(W83781D_INIT_IN_MAX_3));
+ w83781d_write_value(client, W83781D_REG_IN_MIN(4),
+ IN_TO_REG(W83781D_INIT_IN_MIN_4));
+ w83781d_write_value(client, W83781D_REG_IN_MAX(4),
+ IN_TO_REG(W83781D_INIT_IN_MAX_4));
+ if (type == w83781d || type == as99127f) {
+ w83781d_write_value(client, W83781D_REG_IN_MIN(5),
+ IN_TO_REG(W83781D_INIT_IN_MIN_5));
+ w83781d_write_value(client, W83781D_REG_IN_MAX(5),
+ IN_TO_REG(W83781D_INIT_IN_MAX_5));
+ } else {
+ w83781d_write_value(client, W83781D_REG_IN_MIN(5),
+ IN_TO_REG(W83782D_INIT_IN_MIN_5));
+ w83781d_write_value(client, W83781D_REG_IN_MAX(5),
+ IN_TO_REG(W83782D_INIT_IN_MAX_5));
+ }
+ if (type == w83781d || type == as99127f) {
+ w83781d_write_value(client, W83781D_REG_IN_MIN(6),
+ IN_TO_REG(W83781D_INIT_IN_MIN_6));
+ w83781d_write_value(client, W83781D_REG_IN_MAX(6),
+ IN_TO_REG(W83781D_INIT_IN_MAX_6));
+ } else {
+ w83781d_write_value(client, W83781D_REG_IN_MIN(6),
+ IN_TO_REG(W83782D_INIT_IN_MIN_6));
+ w83781d_write_value(client, W83781D_REG_IN_MAX(6),
+ IN_TO_REG(W83782D_INIT_IN_MAX_6));
+ }
+ if ((type == w83782d) || (type == w83627hf) ||
+ (type == w83697hf)) {
+ w83781d_write_value(client, W83781D_REG_IN_MIN(7),
+ IN_TO_REG(W83781D_INIT_IN_MIN_7));
+ w83781d_write_value(client, W83781D_REG_IN_MAX(7),
+ IN_TO_REG(W83781D_INIT_IN_MAX_7));
+ w83781d_write_value(client, W83781D_REG_IN_MIN(8),
+ IN_TO_REG(W83781D_INIT_IN_MIN_8));
+ w83781d_write_value(client, W83781D_REG_IN_MAX(8),
+ IN_TO_REG(W83781D_INIT_IN_MAX_8));
+ w83781d_write_value(client, W83781D_REG_VBAT,
+ (w83781d_read_value
+ (client,
+ W83781D_REG_VBAT) | 0x01));
+ }
+ w83781d_write_value(client, W83781D_REG_FAN_MIN(1),
+ FAN_TO_REG(W83781D_INIT_FAN_MIN_1, 2));
+ w83781d_write_value(client, W83781D_REG_FAN_MIN(2),
+ FAN_TO_REG(W83781D_INIT_FAN_MIN_2, 2));
+ if (type != w83697hf) {
+ w83781d_write_value(client, W83781D_REG_FAN_MIN(3),
+ FAN_TO_REG(W83781D_INIT_FAN_MIN_3,
+ 2));
+ }
+
+ w83781d_write_value(client, W83781D_REG_TEMP_OVER(1),
+ TEMP_TO_REG(W83781D_INIT_TEMP_OVER));
+ w83781d_write_value(client, W83781D_REG_TEMP_HYST(1),
+ TEMP_TO_REG(W83781D_INIT_TEMP_HYST));
+
+ if (type == as99127f) {
+ w83781d_write_value(client, W83781D_REG_TEMP_OVER(2),
+ AS99127_TEMP_ADD_TO_REG
+ (W83781D_INIT_TEMP2_OVER));
+ w83781d_write_value(client, W83781D_REG_TEMP_HYST(2),
+ AS99127_TEMP_ADD_TO_REG
+ (W83781D_INIT_TEMP2_HYST));
+ } else {
+ w83781d_write_value(client, W83781D_REG_TEMP_OVER(2),
+ TEMP_ADD_TO_REG
+ (W83781D_INIT_TEMP2_OVER));
+ w83781d_write_value(client, W83781D_REG_TEMP_HYST(2),
+ TEMP_ADD_TO_REG
+ (W83781D_INIT_TEMP2_HYST));
+ }
+ w83781d_write_value(client, W83781D_REG_TEMP2_CONFIG, 0x00);
+
+ if (type == as99127f) {
+ w83781d_write_value(client, W83781D_REG_TEMP_OVER(3),
+ AS99127_TEMP_ADD_TO_REG
+ (W83781D_INIT_TEMP3_OVER));
+ w83781d_write_value(client, W83781D_REG_TEMP_HYST(3),
+ AS99127_TEMP_ADD_TO_REG
+ (W83781D_INIT_TEMP3_HYST));
+ } else if (type != w83783s && type != w83697hf) {
+ w83781d_write_value(client, W83781D_REG_TEMP_OVER(3),
+ TEMP_ADD_TO_REG
+ (W83781D_INIT_TEMP3_OVER));
+ w83781d_write_value(client, W83781D_REG_TEMP_HYST(3),
+ TEMP_ADD_TO_REG
+ (W83781D_INIT_TEMP3_HYST));
+ }
+ if (type != w83783s && type != w83697hf) {
+ w83781d_write_value(client, W83781D_REG_TEMP3_CONFIG,
+ 0x00);
+ }
+ if (type != w83781d) {
+ /* enable comparator mode for temp2 and temp3 so
+ alarm indication will work correctly */
+ w83781d_write_value(client, W83781D_REG_IRQ, 0x41);
+ for (i = 0; i < 3; i++)
+ data->pwmenable[i] = 1;
+ }
+ }
+
+ /* Start monitoring */
+ w83781d_write_value(client, W83781D_REG_CONFIG,
+ (w83781d_read_value(client,
+ W83781D_REG_CONFIG) & 0xf7)
+ | 0x01);
+}
+
+static void
+w83781d_update_client(struct i2c_client *client)
+{
+ struct w83781d_data *data = i2c_get_clientdata(client);
+ int i;
+
+ down(&data->update_lock);
+
+ if (time_after
+ (jiffies - data->last_updated, (unsigned long) (HZ + HZ / 2))
+ || time_before(jiffies, data->last_updated) || !data->valid) {
+ pr_debug(KERN_DEBUG "Starting device update\n");
+
+ for (i = 0; i <= 8; i++) {
+ if ((data->type == w83783s || data->type == w83697hf)
+ && (i == 1))
+ continue; /* 783S has no in1 */
+ data->in[i] =
+ w83781d_read_value(client, W83781D_REG_IN(i));
+ data->in_min[i] =
+ w83781d_read_value(client, W83781D_REG_IN_MIN(i));
+ data->in_max[i] =
+ w83781d_read_value(client, W83781D_REG_IN_MAX(i));
+ if ((data->type != w83782d) && (data->type != w83697hf)
+ && (data->type != w83627hf) && (i == 6))
+ break;
+ }
+ for (i = 1; i <= 3; i++) {
+ data->fan[i - 1] =
+ w83781d_read_value(client, W83781D_REG_FAN(i));
+ data->fan_min[i - 1] =
+ w83781d_read_value(client, W83781D_REG_FAN_MIN(i));
+ }
+ if (data->type != w83781d) {
+ for (i = 1; i <= 4; i++) {
+ data->pwm[i - 1] =
+ w83781d_read_value(client,
+ W83781D_REG_PWM(i));
+ if (((data->type == w83783s)
+ || (data->type == w83627hf)
+ || (data->type == as99127f)
+ || (data->type == w83697hf)
+ || ((data->type == w83782d)
+ && i2c_is_isa_client(client)))
+ && i == 2)
+ break;
+ }
+ }
+
+ data->temp = w83781d_read_value(client, W83781D_REG_TEMP(1));
+ data->temp_min =
+ w83781d_read_value(client, W83781D_REG_TEMP_OVER(1));
+ data->temp_max =
+ w83781d_read_value(client, W83781D_REG_TEMP_HYST(1));
+ data->temp_add[0] =
+ w83781d_read_value(client, W83781D_REG_TEMP(2));
+ data->temp_max_add[0] =
+ w83781d_read_value(client, W83781D_REG_TEMP_OVER(2));
+ data->temp_min_add[0] =
+ w83781d_read_value(client, W83781D_REG_TEMP_HYST(2));
+ if (data->type != w83783s && data->type != w83697hf) {
+ data->temp_add[1] =
+ w83781d_read_value(client, W83781D_REG_TEMP(3));
+ data->temp_max_add[1] =
+ w83781d_read_value(client,
+ W83781D_REG_TEMP_OVER(3));
+ data->temp_min_add[1] =
+ w83781d_read_value(client,
+ W83781D_REG_TEMP_HYST(3));
+ }
+ i = w83781d_read_value(client, W83781D_REG_VID_FANDIV);
+ if (data->type != w83697hf) {
+ data->vid = i & 0x0f;
+ data->vid |=
+ (w83781d_read_value(client, W83781D_REG_CHIPID) &
+ 0x01)
+ << 4;
+ }
+ data->fan_div[0] = (i >> 4) & 0x03;
+ data->fan_div[1] = (i >> 6) & 0x03;
+ if (data->type != w83697hf) {
+ data->fan_div[2] = (w83781d_read_value(client,
+ W83781D_REG_PIN)
+ >> 6) & 0x03;
+ }
+ if ((data->type != w83781d) && (data->type != as99127f)) {
+ i = w83781d_read_value(client, W83781D_REG_VBAT);
+ data->fan_div[0] |= (i >> 3) & 0x04;
+ data->fan_div[1] |= (i >> 4) & 0x04;
+ if (data->type != w83697hf)
+ data->fan_div[2] |= (i >> 5) & 0x04;
+ }
+ data->alarms =
+ w83781d_read_value(client,
+ W83781D_REG_ALARM1) +
+ (w83781d_read_value(client, W83781D_REG_ALARM2) << 8);
+ if ((data->type == w83782d) || (data->type == w83627hf)) {
+ data->alarms |=
+ w83781d_read_value(client,
+ W83781D_REG_ALARM3) << 16;
+ }
+ i = w83781d_read_value(client, W83781D_REG_BEEP_INTS2);
+ data->beep_enable = i >> 7;
+ data->beep_mask = ((i & 0x7f) << 8) +
+ w83781d_read_value(client, W83781D_REG_BEEP_INTS1);
+ if ((data->type != w83781d) && (data->type != as99127f)) {
+ data->beep_mask |=
+ w83781d_read_value(client,
+ W83781D_REG_BEEP_INTS3) << 16;
+ }
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+
+ up(&data->update_lock);
+}
+
+static int __init
+sensors_w83781d_init(void)
+{
+ return i2c_add_driver(&w83781d_driver);
+}
+
+static void __exit
+sensors_w83781d_exit(void)
+{
+ i2c_del_driver(&w83781d_driver);
+}
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
+ "Philip Edelbrock <phil@netroedge.com>, "
+ "and Mark Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("W83781D driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_w83781d_init);
+module_exit(sensors_w83781d_exit);
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index e838d1922b45..065bdb63a0c8 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -29,7 +29,6 @@
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
-#include <linux/proc_fs.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/seq_file.h>
@@ -46,15 +45,6 @@ static DECLARE_MUTEX(core_lists);
/**** debug level */
static int i2c_debug;
-#ifdef CONFIG_PROC_FS
-static int i2cproc_register(struct i2c_adapter *adap, int bus);
-static void i2cproc_remove(int bus);
-#else
-# define i2cproc_register(adap, bus) 0
-# define i2cproc_remove(bus) do { } while (0)
-#endif /* CONFIG_PROC_FS */
-
-
int i2c_device_probe(struct device *dev)
{
return -ENODEV;
@@ -98,10 +88,6 @@ int i2c_add_adapter(struct i2c_adapter *adap)
goto out_unlock;
}
- res = i2cproc_register(adap, i);
- if (res)
- goto out_unlock;
-
adapters[i] = adap;
init_MUTEX(&adap->bus);
@@ -180,8 +166,6 @@ int i2c_del_adapter(struct i2c_adapter *adap)
}
}
- i2cproc_remove(i);
-
/* clean up the sysfs representation */
device_unregister(&adap->dev);
@@ -392,7 +376,8 @@ int i2c_attach_client(struct i2c_client *client)
client->dev.driver = &client->driver->driver;
client->dev.bus = &i2c_bus_type;
- snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id), "i2c_dev_%d", i);
+ snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),
+ "%d-%04x", i2c_adapter_id(adapter), client->addr);
printk("registering %s\n", client->dev.bus_id);
device_register(&client->dev);
@@ -494,173 +479,6 @@ int i2c_release_client(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PROC_FS
-/* This function generates the output for /proc/bus/i2c-? */
-static ssize_t i2cproc_bus_read(struct file *file, char *buf,
- size_t count, loff_t *ppos)
-{
- struct inode *inode = file->f_dentry->d_inode;
- char *kbuf;
- struct i2c_client *client;
- int i,j,k,order_nr,len=0;
- size_t len_total;
- int order[I2C_CLIENT_MAX];
-#define OUTPUT_LENGTH_PER_LINE 70
-
- len_total = file->f_pos + count;
- if (len_total > (I2C_CLIENT_MAX * OUTPUT_LENGTH_PER_LINE) )
- /* adjust to maximum file size */
- len_total = (I2C_CLIENT_MAX * OUTPUT_LENGTH_PER_LINE);
- for (i = 0; i < I2C_ADAP_MAX; i++)
- if (adapters[i]->inode == inode->i_ino) {
- /* We need a bit of slack in the kernel buffer; this makes the
- sprintf safe. */
- if (! (kbuf = kmalloc(len_total +
- OUTPUT_LENGTH_PER_LINE,
- GFP_KERNEL)))
- return -ENOMEM;
- /* Order will hold the indexes of the clients
- sorted by address */
- order_nr=0;
- for (j = 0; j < I2C_CLIENT_MAX; j++) {
- if ((client = adapters[i]->clients[j]) &&
- (client->driver->id != I2C_DRIVERID_I2CDEV)) {
- for(k = order_nr;
- (k > 0) &&
- adapters[i]->clients[order[k-1]]->
- addr > client->addr;
- k--)
- order[k] = order[k-1];
- order[k] = j;
- order_nr++;
- }
- }
-
-
- for (j = 0; (j < order_nr) && (len < len_total); j++) {
- client = adapters[i]->clients[order[j]];
- len += sprintf(kbuf+len,"%02x\t%-32s\t%-32s\n",
- client->addr,
- client->dev.name,
- client->driver->name);
- }
- len = len - file->f_pos;
- if (len > count)
- len = count;
- if (len < 0)
- len = 0;
- if (copy_to_user (buf,kbuf+file->f_pos, len)) {
- kfree(kbuf);
- return -EFAULT;
- }
- file->f_pos += len;
- kfree(kbuf);
- return len;
- }
- return -ENOENT;
-}
-
-static struct file_operations i2cproc_operations = {
- .read = i2cproc_bus_read,
-};
-
-/* This function generates the output for /proc/bus/i2c */
-static int bus_i2c_show(struct seq_file *s, void *p)
-{
- int i;
-
- down(&core_lists);
- for (i = 0; i < I2C_ADAP_MAX; i++) {
- struct i2c_adapter *adapter = adapters[i];
-
- if (!adapter)
- continue;
-
- seq_printf(s, "i2c-%d\t", i);
-
- if (adapter->algo->smbus_xfer) {
- if (adapter->algo->master_xfer)
- seq_printf(s, "smbus/i2c");
- else
- seq_printf(s, "smbus ");
- } else if (adapter->algo->master_xfer)
- seq_printf(s ,"i2c ");
- else
- seq_printf(s, "dummy ");
-
- seq_printf(s, "\t%-32s\t%-32s\n",
- adapter->dev.name, adapter->algo->name);
- }
- up(&core_lists);
-
- return 0;
-}
-
-static int bus_i2c_open(struct inode *inode, struct file *file)
-{
- return single_open(file, bus_i2c_show, NULL);
-}
-
-static struct file_operations bus_i2c_fops = {
- .open = bus_i2c_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
-
-static int i2cproc_register(struct i2c_adapter *adap, int bus)
-{
- struct proc_dir_entry *proc_entry;
- char name[8];
-
- sprintf(name, "i2c-%d", bus);
-
- proc_entry = create_proc_entry(name, 0, proc_bus);
- if (!proc_entry)
- goto fail;
-
- proc_entry->proc_fops = &i2cproc_operations;
- proc_entry->owner = adap->owner;
- adap->inode = proc_entry->low_ino;
- return 0;
- fail:
- printk(KERN_ERR "i2c-core.o: Could not create /proc/bus/%s\n", name);
- return -ENOENT;
-}
-
-static void i2cproc_remove(int bus)
-{
- char name[8];
-
- sprintf(name,"i2c-%d", bus);
- remove_proc_entry(name, proc_bus);
-}
-
-static int __init i2cproc_init(void)
-{
- struct proc_dir_entry *proc_bus_i2c;
-
- proc_bus_i2c = create_proc_entry("i2c", 0, proc_bus);
- if (!proc_bus_i2c)
- goto fail;
- proc_bus_i2c->proc_fops = &bus_i2c_fops;
- proc_bus_i2c->owner = THIS_MODULE;
- return 0;
-
- fail:
- printk(KERN_ERR "i2c-core.o: Could not create /proc/bus/i2c");
- return -ENOENT;
-}
-
-static void __exit i2cproc_cleanup(void)
-{
- remove_proc_entry("i2c",proc_bus);
-}
-#else
-static int __init i2cproc_init(void) { return 0; }
-static void __exit i2cproc_cleanup(void) { }
-#endif /* CONFIG_PROC_FS */
-
/* match always succeeds, as we want the probe() to tell if we really accept this match */
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
@@ -675,13 +493,11 @@ struct bus_type i2c_bus_type = {
static int __init i2c_init(void)
{
- bus_register(&i2c_bus_type);
- return i2cproc_init();
+ return bus_register(&i2c_bus_type);
}
static void __exit i2c_exit(void)
{
- i2cproc_cleanup();
bus_unregister(&i2c_bus_type);
}
diff --git a/drivers/i2c/i2c-proc.c b/drivers/i2c/i2c-proc.c
deleted file mode 100644
index e8b70eb690a6..000000000000
--- a/drivers/i2c/i2c-proc.c
+++ /dev/null
@@ -1,733 +0,0 @@
-/*
- i2c-proc.c - Part of lm_sensors, Linux kernel modules for hardware
- monitoring
- Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl> and
- Mark D. Studebaker <mdsxyz123@yahoo.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-/*
- This driver puts entries in /proc/sys/dev/sensors for each I2C device
-*/
-
-/* #define DEBUG 1 */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/ctype.h>
-#include <linux/sysctl.h>
-#include <linux/proc_fs.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/i2c.h>
-#include <linux/i2c-proc.h>
-#include <asm/uaccess.h>
-
-static int i2c_parse_reals(int *nrels, void *buffer, int bufsize,
- long *results, int magnitude);
-static int i2c_write_reals(int nrels, void *buffer, size_t *bufsize,
- long *results, int magnitude);
-static int i2c_proc_chips(ctl_table * ctl, int write,
- struct file *filp, void *buffer,
- size_t * lenp);
-static int i2c_sysctl_chips(ctl_table * table, int *name, int nlen,
- void *oldval, size_t * oldlenp,
- void *newval, size_t newlen,
- void **context);
-
-#define SENSORS_ENTRY_MAX 20
-static struct ctl_table_header *i2c_entries[SENSORS_ENTRY_MAX];
-
-static struct i2c_client *i2c_clients[SENSORS_ENTRY_MAX];
-
-static ctl_table i2c_proc_dev_sensors[] = {
- {SENSORS_CHIPS, "chips", NULL, 0, 0644, NULL, &i2c_proc_chips,
- &i2c_sysctl_chips},
- {0}
-};
-
-static ctl_table i2c_proc_dev[] = {
- {DEV_SENSORS, "sensors", NULL, 0, 0555, i2c_proc_dev_sensors},
- {0},
-};
-
-
-static ctl_table i2c_proc[] = {
- {CTL_DEV, "dev", NULL, 0, 0555, i2c_proc_dev},
- {0}
-};
-
-
-static struct ctl_table_header *i2c_proc_header;
-
-/* This returns a nice name for a new directory; for example lm78-isa-0310
- (for a LM78 chip on the ISA bus at port 0x310), or lm75-i2c-3-4e (for
- a LM75 chip on the third i2c bus at address 0x4e).
- name is allocated first. */
-static char *generate_name(struct i2c_client *client, const char *prefix)
-{
- struct i2c_adapter *adapter = client->adapter;
- int addr = client->addr;
- char name_buffer[50], *name;
-
- if (i2c_is_isa_adapter(adapter)) {
- sprintf(name_buffer, "%s-isa-%04x", prefix, addr);
- } else if (adapter->algo->smbus_xfer || adapter->algo->master_xfer) {
- int id = i2c_adapter_id(adapter);
- if (id < 0)
- return ERR_PTR(-ENOENT);
- sprintf(name_buffer, "%s-i2c-%d-%02x", prefix, id, addr);
- } else { /* dummy adapter, generate prefix */
- int end, i;
-
- sprintf(name_buffer, "%s-", prefix);
- end = strlen(name_buffer);
-
- for (i = 0; i < 32; i++) {
- if (adapter->algo->name[i] == ' ')
- break;
- name_buffer[end++] = tolower(adapter->algo->name[i]);
- }
-
- name_buffer[end] = 0;
- sprintf(name_buffer + end, "-%04x", addr);
- }
-
- name = kmalloc(strlen(name_buffer) + 1, GFP_KERNEL);
- if (unlikely(!name))
- return ERR_PTR(-ENOMEM);
- strcpy(name, name_buffer);
- return name;
-}
-
-/* This rather complex function must be called when you want to add an entry
- to /proc/sys/dev/sensors/chips. It also creates a new directory within
- /proc/sys/dev/sensors/.
- ctl_template should be a template of the newly created directory. It is
- copied in memory. The extra2 field of each file is set to point to client.
- If any driver wants subdirectories within the newly created directory,
- this function must be updated! */
-int i2c_register_entry(struct i2c_client *client, const char *prefix,
- struct ctl_table *leaf)
-{
- struct { struct ctl_table root[2], dev[2], sensors[2]; } *tbl;
- struct ctl_table_header *hdr;
- struct ctl_table *tmp;
- const char *name;
- int id;
-
- name = generate_name(client, prefix);
- if (IS_ERR(name))
- return PTR_ERR(name);
-
- for (id = 0; id < SENSORS_ENTRY_MAX; id++) {
- if (!i2c_entries[id])
- goto free_slot;
- }
-
- goto out_free_name;
-
- free_slot:
- tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
- if (unlikely(!tbl))
- goto out_free_name;
- memset(tbl, 0, sizeof(*tbl));
-
- for (tmp = leaf; tmp->ctl_name; tmp++)
- tmp->extra2 = client;
-
- tbl->sensors->ctl_name = id+256;
- tbl->sensors->procname = name;
- tbl->sensors->mode = 0555;
- tbl->sensors->child = leaf;
-
- tbl->dev->ctl_name = DEV_SENSORS;
- tbl->dev->procname = "sensors";
- tbl->dev->mode = 0555;
- tbl->dev->child = tbl->sensors;
-
- tbl->root->ctl_name = CTL_DEV;
- tbl->root->procname = "dev";
- tbl->root->mode = 0555;
- tbl->root->child = tbl->dev;
-
- hdr = register_sysctl_table(tbl->root, 0);
- if (unlikely(!hdr))
- goto out_free_tbl;
-
- i2c_entries[id] = hdr;
- i2c_clients[id] = client;
-
- return (id + 256); /* XXX(hch) why?? */
-
- out_free_tbl:
- kfree(tbl);
- out_free_name:
- kfree(name);
- return -ENOMEM;
-}
-
-void i2c_deregister_entry(int id)
-{
- id -= 256;
-
- if (i2c_entries[id]) {
- struct ctl_table_header *hdr = i2c_entries[id];
- struct ctl_table *tbl = hdr->ctl_table;
-
- unregister_sysctl_table(hdr);
- kfree(tbl->child->child->procname);
- kfree(tbl); /* actually the whole anonymous struct */
- }
-
- i2c_entries[id] = NULL;
- i2c_clients[id] = NULL;
-}
-
-static int i2c_proc_chips(ctl_table * ctl, int write, struct file *filp,
- void *buffer, size_t * lenp)
-{
- char BUF[SENSORS_PREFIX_MAX + 30];
- int buflen, curbufsize, i;
- struct ctl_table *client_tbl;
-
- if (write)
- return 0;
-
- /* If buffer is size 0, or we try to read when not at the start, we
- return nothing. Note that I think writing when not at the start
- does not work either, but anyway, this is straight from the kernel
- sources. */
- if (!*lenp || (filp->f_pos && !write)) {
- *lenp = 0;
- return 0;
- }
- curbufsize = 0;
- for (i = 0; i < SENSORS_ENTRY_MAX; i++)
- if (i2c_entries[i]) {
- client_tbl =
- i2c_entries[i]->ctl_table->child->child;
- buflen =
- sprintf(BUF, "%d\t%s\n", client_tbl->ctl_name,
- client_tbl->procname);
- if (buflen + curbufsize > *lenp)
- buflen = *lenp - curbufsize;
- if(copy_to_user(buffer, BUF, buflen))
- return -EFAULT;
- curbufsize += buflen;
- (char *) buffer += buflen;
- }
- *lenp = curbufsize;
- filp->f_pos += curbufsize;
- return 0;
-}
-
-static int i2c_sysctl_chips(ctl_table * table, int *name, int nlen,
- void *oldval, size_t * oldlenp, void *newval,
- size_t newlen, void **context)
-{
- struct i2c_chips_data data;
- int i, oldlen, nrels, maxels,ret=0;
- struct ctl_table *client_tbl;
-
- if (oldval && oldlenp && !((ret = get_user(oldlen, oldlenp))) &&
- oldlen) {
- maxels = oldlen / sizeof(struct i2c_chips_data);
- nrels = 0;
- for (i = 0; (i < SENSORS_ENTRY_MAX) && (nrels < maxels);
- i++)
- if (i2c_entries[i]) {
- client_tbl =
- i2c_entries[i]->ctl_table->child->
- child;
- data.sysctl_id = client_tbl->ctl_name;
- strcpy(data.name, client_tbl->procname);
- if(copy_to_user(oldval, &data,
- sizeof(struct
- i2c_chips_data)))
- return -EFAULT;
- (char *) oldval +=
- sizeof(struct i2c_chips_data);
- nrels++;
- }
- oldlen = nrels * sizeof(struct i2c_chips_data);
- if(put_user(oldlen, oldlenp))
- return -EFAULT;
- }
- return ret;
-}
-
-
-/* This function reads or writes a 'real' value (encoded by the combination
- of an integer and a magnitude, the last is the power of ten the value
- should be divided with) to a /proc/sys directory. To use this function,
- you must (before registering the ctl_table) set the extra2 field to the
- client, and the extra1 field to a function of the form:
- void func(struct i2c_client *client, int operation, int ctl_name,
- int *nrels_mag, long *results)
- This function can be called for three values of operation. If operation
- equals SENSORS_PROC_REAL_INFO, the magnitude should be returned in
- nrels_mag. If operation equals SENSORS_PROC_REAL_READ, values should
- be read into results. nrels_mag should return the number of elements
- read; the maximum number is put in it on entry. Finally, if operation
- equals SENSORS_PROC_REAL_WRITE, the values in results should be
- written to the chip. nrels_mag contains on entry the number of elements
- found.
- In all cases, client points to the client we wish to interact with,
- and ctl_name is the SYSCTL id of the file we are accessing. */
-int i2c_proc_real(ctl_table * ctl, int write, struct file *filp,
- void *buffer, size_t * lenp)
-{
-#define MAX_RESULTS 32
- int mag, nrels = MAX_RESULTS;
- long results[MAX_RESULTS];
- i2c_real_callback callback = ctl->extra1;
- struct i2c_client *client = ctl->extra2;
- int res;
-
- /* If buffer is size 0, or we try to read when not at the start, we
- return nothing. Note that I think writing when not at the start
- does not work either, but anyway, this is straight from the kernel
- sources. */
- if (!*lenp || (filp->f_pos && !write)) {
- *lenp = 0;
- return 0;
- }
-
- /* Get the magnitude */
- callback(client, SENSORS_PROC_REAL_INFO, ctl->ctl_name, &mag,
- NULL);
-
- if (write) {
- /* Read the complete input into results, converting to longs */
- res = i2c_parse_reals(&nrels, buffer, *lenp, results, mag);
- if (res)
- return res;
-
- if (!nrels)
- return 0;
-
- /* Now feed this information back to the client */
- callback(client, SENSORS_PROC_REAL_WRITE, ctl->ctl_name,
- &nrels, results);
-
- filp->f_pos += *lenp;
- return 0;
- } else { /* read */
- /* Get the information from the client into results */
- callback(client, SENSORS_PROC_REAL_READ, ctl->ctl_name,
- &nrels, results);
-
- /* And write them to buffer, converting to reals */
- res = i2c_write_reals(nrels, buffer, lenp, results, mag);
- if (res)
- return res;
- filp->f_pos += *lenp;
- return 0;
- }
-}
-
-/* This function is equivalent to i2c_proc_real, only it interacts with
- the sysctl(2) syscall, and returns no reals, but integers */
-int i2c_sysctl_real(ctl_table * table, int *name, int nlen,
- void *oldval, size_t * oldlenp, void *newval,
- size_t newlen, void **context)
-{
- long results[MAX_RESULTS];
- int oldlen, nrels = MAX_RESULTS,ret=0;
- i2c_real_callback callback = table->extra1;
- struct i2c_client *client = table->extra2;
-
- /* Check if we need to output the old values */
- if (oldval && oldlenp && !((ret=get_user(oldlen, oldlenp))) && oldlen) {
- callback(client, SENSORS_PROC_REAL_READ, table->ctl_name,
- &nrels, results);
-
- /* Note the rounding factor! */
- if (nrels * sizeof(long) < oldlen)
- oldlen = nrels * sizeof(long);
- oldlen = (oldlen / sizeof(long)) * sizeof(long);
- if(copy_to_user(oldval, results, oldlen))
- return -EFAULT;
- if(put_user(oldlen, oldlenp))
- return -EFAULT;
- }
-
- if (newval && newlen) {
- /* Note the rounding factor! */
- newlen -= newlen % sizeof(long);
- nrels = newlen / sizeof(long);
- if(copy_from_user(results, newval, newlen))
- return -EFAULT;
-
- /* Get the new values back to the client */
- callback(client, SENSORS_PROC_REAL_WRITE, table->ctl_name,
- &nrels, results);
- }
- return ret;
-}
-
-
-/* nrels contains initially the maximum number of elements which can be
- put in results, and finally the number of elements actually put there.
- A magnitude of 1 will multiply everything with 10; etc.
- buffer, bufsize is the character buffer we read from and its length.
- results will finally contain the parsed integers.
-
- Buffer should contain several reals, separated by whitespace. A real
- has the following syntax:
- [ Minus ] Digit* [ Dot Digit* ]
- (everything between [] is optional; * means zero or more).
- When the next character is unparsable, everything is skipped until the
- next whitespace.
-
- WARNING! This is tricky code. I have tested it, but there may still be
- hidden bugs in it, even leading to crashes and things!
-*/
-static int i2c_parse_reals(int *nrels, void *buffer, int bufsize,
- long *results, int magnitude)
-{
- int maxels, min, mag;
- long res,ret=0;
- char nextchar = 0;
-
- maxels = *nrels;
- *nrels = 0;
-
- while (bufsize && (*nrels < maxels)) {
-
- /* Skip spaces at the start */
- while (bufsize &&
- !((ret=get_user(nextchar, (char *) buffer))) &&
- isspace((int) nextchar)) {
- bufsize--;
- ((char *) buffer)++;
- }
-
- if (ret)
- return -EFAULT;
- /* Well, we may be done now */
- if (!bufsize)
- return 0;
-
- /* New defaults for our result */
- min = 0;
- res = 0;
- mag = magnitude;
-
- /* Check for a minus */
- if (!((ret=get_user(nextchar, (char *) buffer)))
- && (nextchar == '-')) {
- min = 1;
- bufsize--;
- ((char *) buffer)++;
- }
- if (ret)
- return -EFAULT;
-
- /* Digits before a decimal dot */
- while (bufsize &&
- !((ret=get_user(nextchar, (char *) buffer))) &&
- isdigit((int) nextchar)) {
- res = res * 10 + nextchar - '0';
- bufsize--;
- ((char *) buffer)++;
- }
- if (ret)
- return -EFAULT;
-
- /* If mag < 0, we must actually divide here! */
- while (mag < 0) {
- res = res / 10;
- mag++;
- }
-
- if (bufsize && (nextchar == '.')) {
- /* Skip the dot */
- bufsize--;
- ((char *) buffer)++;
-
- /* Read digits while they are significant */
- while (bufsize && (mag > 0) &&
- !((ret=get_user(nextchar, (char *) buffer))) &&
- isdigit((int) nextchar)) {
- res = res * 10 + nextchar - '0';
- mag--;
- bufsize--;
- ((char *) buffer)++;
- }
- if (ret)
- return -EFAULT;
- }
- /* If we are out of data, but mag > 0, we need to scale here */
- while (mag > 0) {
- res = res * 10;
- mag--;
- }
-
- /* Skip everything until we hit whitespace */
- while (bufsize &&
- !((ret=get_user(nextchar, (char *) buffer))) &&
- isspace((int) nextchar)) {
- bufsize--;
- ((char *) buffer)++;
- }
- if (ret)
- return -EFAULT;
-
- /* Put res in results */
- results[*nrels] = (min ? -1 : 1) * res;
- (*nrels)++;
- }
-
- /* Well, there may be more in the buffer, but we need no more data.
- Ignore anything that is left. */
- return 0;
-}
-
-static int i2c_write_reals(int nrels, void *buffer, size_t *bufsize,
- long *results, int magnitude)
-{
-#define BUFLEN 20
- char BUF[BUFLEN + 1]; /* An individual representation should fit! */
- char printfstr[10];
- int nr = 0;
- int buflen, mag, times;
- int curbufsize = 0;
-
- while ((nr < nrels) && (curbufsize < *bufsize)) {
- mag = magnitude;
-
- if (nr != 0) {
- if(put_user(' ', (char *) buffer))
- return -EFAULT;
- curbufsize++;
- ((char *) buffer)++;
- }
-
- /* Fill BUF with the representation of the next string */
- if (mag <= 0) {
- buflen = sprintf(BUF, "%ld", results[nr]);
- if (buflen < 0) { /* Oops, a sprintf error! */
- *bufsize = 0;
- return -EINVAL;
- }
- while ((mag < 0) && (buflen < BUFLEN)) {
- BUF[buflen++] = '0';
- mag++;
- }
- BUF[buflen] = 0;
- } else {
- times = 1;
- for (times = 1; mag-- > 0; times *= 10);
- if (results[nr] < 0) {
- BUF[0] = '-';
- buflen = 1;
- } else
- buflen = 0;
- strcpy(printfstr, "%ld.%0Xld");
- printfstr[6] = magnitude + '0';
- buflen +=
- sprintf(BUF + buflen, printfstr,
- abs(results[nr]) / times,
- abs(results[nr]) % times);
- if (buflen < 0) { /* Oops, a sprintf error! */
- *bufsize = 0;
- return -EINVAL;
- }
- }
-
- /* Now copy it to the user-space buffer */
- if (buflen + curbufsize > *bufsize)
- buflen = *bufsize - curbufsize;
- if(copy_to_user(buffer, BUF, buflen))
- return -EFAULT;
- curbufsize += buflen;
- (char *) buffer += buflen;
-
- nr++;
- }
- if (curbufsize < *bufsize) {
- if(put_user('\n', (char *) buffer))
- return -EFAULT;
- curbufsize++;
- }
- *bufsize = curbufsize;
- return 0;
-}
-
-
-/* Very inefficient for ISA detects, and won't work for 10-bit addresses! */
-int i2c_detect(struct i2c_adapter *adapter,
- struct i2c_address_data *address_data,
- i2c_found_addr_proc * found_proc)
-{
- int addr, i, found, j, err;
- struct i2c_force_data *this_force;
- int is_isa = i2c_is_isa_adapter(adapter);
- int adapter_id =
- is_isa ? SENSORS_ISA_BUS : i2c_adapter_id(adapter);
-
- /* Forget it if we can't probe using SMBUS_QUICK */
- if ((!is_isa) &&
- !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK))
- return -1;
-
- for (addr = 0x00; addr <= (is_isa ? 0xffff : 0x7f); addr++) {
- /* XXX: WTF is going on here??? */
- if ((is_isa && check_region(addr, 1)) ||
- (!is_isa && i2c_check_addr(adapter, addr)))
- continue;
-
- /* If it is in one of the force entries, we don't do any
- detection at all */
- found = 0;
- for (i = 0; !found && (this_force = address_data->forces + i, this_force->force); i++) {
- for (j = 0; !found && (this_force->force[j] != SENSORS_I2C_END); j += 2) {
- if ( ((adapter_id == this_force->force[j]) ||
- ((this_force->force[j] == SENSORS_ANY_I2C_BUS) && !is_isa)) &&
- (addr == this_force->force[j + 1]) ) {
- dev_dbg(&adapter->dev, "found force parameter for adapter %d, addr %04x\n", adapter_id, addr);
- if ((err = found_proc(adapter, addr, 0, this_force->kind)))
- return err;
- found = 1;
- }
- }
- }
- if (found)
- continue;
-
- /* If this address is in one of the ignores, we can forget about it
- right now */
- for (i = 0; !found && (address_data->ignore[i] != SENSORS_I2C_END); i += 2) {
- if ( ((adapter_id == address_data->ignore[i]) ||
- ((address_data->ignore[i] == SENSORS_ANY_I2C_BUS) &&
- !is_isa)) &&
- (addr == address_data->ignore[i + 1])) {
- dev_dbg(&adapter->dev, "found ignore parameter for adapter %d, addr %04x\n", adapter_id, addr);
- found = 1;
- }
- }
- for (i = 0; !found && (address_data->ignore_range[i] != SENSORS_I2C_END); i += 3) {
- if ( ((adapter_id == address_data->ignore_range[i]) ||
- ((address_data-> ignore_range[i] == SENSORS_ANY_I2C_BUS) &
- !is_isa)) &&
- (addr >= address_data->ignore_range[i + 1]) &&
- (addr <= address_data->ignore_range[i + 2])) {
- dev_dbg(&adapter->dev, "found ignore_range parameter for adapter %d, addr %04x\n", adapter_id, addr);
- found = 1;
- }
- }
- if (found)
- continue;
-
- /* Now, we will do a detection, but only if it is in the normal or
- probe entries */
- if (is_isa) {
- for (i = 0; !found && (address_data->normal_isa[i] != SENSORS_ISA_END); i += 1) {
- if (addr == address_data->normal_isa[i]) {
- dev_dbg(&adapter->dev, "found normal isa entry for adapter %d, addr %04x\n", adapter_id, addr);
- found = 1;
- }
- }
- for (i = 0; !found && (address_data->normal_isa_range[i] != SENSORS_ISA_END); i += 3) {
- if ((addr >= address_data->normal_isa_range[i]) &&
- (addr <= address_data->normal_isa_range[i + 1]) &&
- ((addr - address_data->normal_isa_range[i]) % address_data->normal_isa_range[i + 2] == 0)) {
- dev_dbg(&adapter->dev, "found normal isa_range entry for adapter %d, addr %04x", adapter_id, addr);
- found = 1;
- }
- }
- } else {
- for (i = 0; !found && (address_data->normal_i2c[i] != SENSORS_I2C_END); i += 1) {
- if (addr == address_data->normal_i2c[i]) {
- found = 1;
- dev_dbg(&adapter->dev, "found normal i2c entry for adapter %d, addr %02x", adapter_id, addr);
- }
- }
- for (i = 0; !found && (address_data->normal_i2c_range[i] != SENSORS_I2C_END); i += 2) {
- if ((addr >= address_data->normal_i2c_range[i]) &&
- (addr <= address_data->normal_i2c_range[i + 1])) {
- dev_dbg(&adapter->dev, "found normal i2c_range entry for adapter %d, addr %04x\n", adapter_id, addr);
- found = 1;
- }
- }
- }
-
- for (i = 0;
- !found && (address_data->probe[i] != SENSORS_I2C_END);
- i += 2) {
- if (((adapter_id == address_data->probe[i]) ||
- ((address_data->
- probe[i] == SENSORS_ANY_I2C_BUS) & !is_isa))
- && (addr == address_data->probe[i + 1])) {
- dev_dbg(&adapter->dev, "found probe parameter for adapter %d, addr %04x\n", adapter_id, addr);
- found = 1;
- }
- }
- for (i = 0; !found && (address_data->probe_range[i] != SENSORS_I2C_END); i += 3) {
- if ( ((adapter_id == address_data->probe_range[i]) ||
- ((address_data->probe_range[i] == SENSORS_ANY_I2C_BUS) & !is_isa)) &&
- (addr >= address_data->probe_range[i + 1]) &&
- (addr <= address_data->probe_range[i + 2])) {
- found = 1;
- dev_dbg(&adapter->dev, "found probe_range parameter for adapter %d, addr %04x\n", adapter_id, addr);
- }
- }
- if (!found)
- continue;
-
- /* OK, so we really should examine this address. First check
- whether there is some client here at all! */
- if (is_isa ||
- (i2c_smbus_xfer (adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL) >= 0))
- if ((err = found_proc(adapter, addr, 0, -1)))
- return err;
- }
- return 0;
-}
-
-static int __init i2c_proc_init(void)
-{
- printk(KERN_INFO "i2c-proc.o version %s (%s)\n", I2C_VERSION, I2C_DATE);
- if (!
- (i2c_proc_header =
- register_sysctl_table(i2c_proc, 0))) {
- printk(KERN_ERR "i2c-proc.o: error: sysctl interface not supported by kernel!\n");
- return -EPERM;
- }
- i2c_proc_header->ctl_table->child->de->owner = THIS_MODULE;
- return 0;
-}
-
-static void __exit i2c_proc_exit(void)
-{
- unregister_sysctl_table(i2c_proc_header);
-}
-
-EXPORT_SYMBOL(i2c_register_entry);
-EXPORT_SYMBOL(i2c_deregister_entry);
-EXPORT_SYMBOL(i2c_proc_real);
-EXPORT_SYMBOL(i2c_sysctl_real);
-EXPORT_SYMBOL(i2c_detect);
-
-MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
-MODULE_DESCRIPTION("i2c-proc driver");
-MODULE_LICENSE("GPL");
-
-module_init(i2c_proc_init);
-module_exit(i2c_proc_exit);
diff --git a/drivers/i2c/i2c-sensor.c b/drivers/i2c/i2c-sensor.c
new file mode 100644
index 000000000000..1e0ae6a02b87
--- /dev/null
+++ b/drivers/i2c/i2c-sensor.c
@@ -0,0 +1,182 @@
+/*
+ i2c-sensor.c - Part of lm_sensors, Linux kernel modules for hardware
+ monitoring
+ Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl> and
+ Mark D. Studebaker <mdsxyz123@yahoo.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* #define DEBUG 1 */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/sysctl.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+#include <asm/uaccess.h>
+
+
+/* Very inefficient for ISA detects, and won't work for 10-bit addresses! */
+int i2c_detect(struct i2c_adapter *adapter,
+ struct i2c_address_data *address_data,
+ i2c_found_addr_proc * found_proc)
+{
+ int addr, i, found, j, err;
+ struct i2c_force_data *this_force;
+ int is_isa = i2c_is_isa_adapter(adapter);
+ int adapter_id =
+ is_isa ? SENSORS_ISA_BUS : i2c_adapter_id(adapter);
+
+ /* Forget it if we can't probe using SMBUS_QUICK */
+ if ((!is_isa) &&
+ !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK))
+ return -1;
+
+ for (addr = 0x00; addr <= (is_isa ? 0xffff : 0x7f); addr++) {
+ /* XXX: WTF is going on here??? */
+ if ((is_isa && check_region(addr, 1)) ||
+ (!is_isa && i2c_check_addr(adapter, addr)))
+ continue;
+
+ /* If it is in one of the force entries, we don't do any
+ detection at all */
+ found = 0;
+ for (i = 0; !found && (this_force = address_data->forces + i, this_force->force); i++) {
+ for (j = 0; !found && (this_force->force[j] != SENSORS_I2C_END); j += 2) {
+ if ( ((adapter_id == this_force->force[j]) ||
+ ((this_force->force[j] == SENSORS_ANY_I2C_BUS) && !is_isa)) &&
+ (addr == this_force->force[j + 1]) ) {
+ dev_dbg(&adapter->dev, "found force parameter for adapter %d, addr %04x\n", adapter_id, addr);
+ if ((err = found_proc(adapter, addr, this_force->kind)))
+ return err;
+ found = 1;
+ }
+ }
+ }
+ if (found)
+ continue;
+
+ /* If this address is in one of the ignores, we can forget about it
+ right now */
+ for (i = 0; !found && (address_data->ignore[i] != SENSORS_I2C_END); i += 2) {
+ if ( ((adapter_id == address_data->ignore[i]) ||
+ ((address_data->ignore[i] == SENSORS_ANY_I2C_BUS) &&
+ !is_isa)) &&
+ (addr == address_data->ignore[i + 1])) {
+ dev_dbg(&adapter->dev, "found ignore parameter for adapter %d, addr %04x\n", adapter_id, addr);
+ found = 1;
+ }
+ }
+ for (i = 0; !found && (address_data->ignore_range[i] != SENSORS_I2C_END); i += 3) {
+ if ( ((adapter_id == address_data->ignore_range[i]) ||
+ ((address_data-> ignore_range[i] == SENSORS_ANY_I2C_BUS) &
+ !is_isa)) &&
+ (addr >= address_data->ignore_range[i + 1]) &&
+ (addr <= address_data->ignore_range[i + 2])) {
+ dev_dbg(&adapter->dev, "found ignore_range parameter for adapter %d, addr %04x\n", adapter_id, addr);
+ found = 1;
+ }
+ }
+ if (found)
+ continue;
+
+ /* Now, we will do a detection, but only if it is in the normal or
+ probe entries */
+ if (is_isa) {
+ for (i = 0; !found && (address_data->normal_isa[i] != SENSORS_ISA_END); i += 1) {
+ if (addr == address_data->normal_isa[i]) {
+ dev_dbg(&adapter->dev, "found normal isa entry for adapter %d, addr %04x\n", adapter_id, addr);
+ found = 1;
+ }
+ }
+ for (i = 0; !found && (address_data->normal_isa_range[i] != SENSORS_ISA_END); i += 3) {
+ if ((addr >= address_data->normal_isa_range[i]) &&
+ (addr <= address_data->normal_isa_range[i + 1]) &&
+ ((addr - address_data->normal_isa_range[i]) % address_data->normal_isa_range[i + 2] == 0)) {
+ dev_dbg(&adapter->dev, "found normal isa_range entry for adapter %d, addr %04x", adapter_id, addr);
+ found = 1;
+ }
+ }
+ } else {
+ for (i = 0; !found && (address_data->normal_i2c[i] != SENSORS_I2C_END); i += 1) {
+ if (addr == address_data->normal_i2c[i]) {
+ found = 1;
+ dev_dbg(&adapter->dev, "found normal i2c entry for adapter %d, addr %02x", adapter_id, addr);
+ }
+ }
+ for (i = 0; !found && (address_data->normal_i2c_range[i] != SENSORS_I2C_END); i += 2) {
+ if ((addr >= address_data->normal_i2c_range[i]) &&
+ (addr <= address_data->normal_i2c_range[i + 1])) {
+ dev_dbg(&adapter->dev, "found normal i2c_range entry for adapter %d, addr %04x\n", adapter_id, addr);
+ found = 1;
+ }
+ }
+ }
+
+ for (i = 0;
+ !found && (address_data->probe[i] != SENSORS_I2C_END);
+ i += 2) {
+ if (((adapter_id == address_data->probe[i]) ||
+ ((address_data->
+ probe[i] == SENSORS_ANY_I2C_BUS) & !is_isa))
+ && (addr == address_data->probe[i + 1])) {
+ dev_dbg(&adapter->dev, "found probe parameter for adapter %d, addr %04x\n", adapter_id, addr);
+ found = 1;
+ }
+ }
+ for (i = 0; !found && (address_data->probe_range[i] != SENSORS_I2C_END); i += 3) {
+ if ( ((adapter_id == address_data->probe_range[i]) ||
+ ((address_data->probe_range[i] == SENSORS_ANY_I2C_BUS) & !is_isa)) &&
+ (addr >= address_data->probe_range[i + 1]) &&
+ (addr <= address_data->probe_range[i + 2])) {
+ found = 1;
+ dev_dbg(&adapter->dev, "found probe_range parameter for adapter %d, addr %04x\n", adapter_id, addr);
+ }
+ }
+ if (!found)
+ continue;
+
+ /* OK, so we really should examine this address. First check
+ whether there is some client here at all! */
+ if (is_isa ||
+ (i2c_smbus_xfer (adapter, addr, 0, 0, 0, I2C_SMBUS_QUICK, NULL) >= 0))
+ if ((err = found_proc(adapter, addr, -1)))
+ return err;
+ }
+ return 0;
+}
+
+static int __init i2c_sensor_init(void)
+{
+ return 0;
+}
+
+static void __exit i2c_sensor_exit(void)
+{
+}
+
+EXPORT_SYMBOL(i2c_detect);
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_DESCRIPTION("i2c-sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_sensor_init);
+module_exit(i2c_sensor_exit);
diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c
index 9d8d38947607..71acec007785 100644
--- a/drivers/media/video/adv7175.c
+++ b/drivers/media/video/adv7175.c
@@ -231,7 +231,7 @@ int adv717x_probe(struct i2c_adapter *adap)
static int adv717x_detach(struct i2c_client *client)
{
i2c_detach_client(client);
- i2c_get_clientdata(client);
+ kfree(i2c_get_clientdata(client));
kfree(client);
return 0;
}
diff --git a/drivers/media/video/tvmixer.c b/drivers/media/video/tvmixer.c
index ff50ba8bb249..ddc82a5a03fa 100644
--- a/drivers/media/video/tvmixer.c
+++ b/drivers/media/video/tvmixer.c
@@ -87,7 +87,7 @@ static int tvmixer_ioctl(struct inode *inode, struct file *file, unsigned int cm
if (cmd == SOUND_MIXER_INFO) {
mixer_info info;
strncpy(info.id, "tv card", sizeof(info.id));
- strncpy(info.name, client->name, sizeof(info.name));
+ strncpy(info.name, client->dev.name, sizeof(info.name));
info.modify_counter = 42 /* FIXME */;
if (copy_to_user((void *)arg, &info, sizeof(info)))
return -EFAULT;
@@ -96,7 +96,7 @@ static int tvmixer_ioctl(struct inode *inode, struct file *file, unsigned int cm
if (cmd == SOUND_OLD_MIXER_INFO) {
_old_mixer_info info;
strncpy(info.id, "tv card", sizeof(info.id));
- strncpy(info.name, client->name, sizeof(info.name));
+ strncpy(info.name, client->dev.name, sizeof(info.name));
if (copy_to_user((void *)arg, &info, sizeof(info)))
return -EFAULT;
return 0;
@@ -237,7 +237,7 @@ static int tvmixer_adapters(struct i2c_adapter *adap)
int i;
if (debug)
- printk("tvmixer: adapter %s\n",adap->name);
+ printk("tvmixer: adapter %s\n",adap->dev.name);
for (i=0; i<I2C_CLIENT_MAX; i++) {
if (!adap->clients[i])
continue;
@@ -261,10 +261,10 @@ static int tvmixer_clients(struct i2c_client *client)
/* ignore that one */
if (debug)
printk("tvmixer: %s is not a tv card\n",
- client->adapter->name);
+ client->adapter->dev.name);
return -1;
}
- printk("tvmixer: debug: %s\n",client->name);
+ printk("tvmixer: debug: %s\n",client->dev.name);
/* unregister ?? */
for (i = 0; i < DEV_MAX; i++) {
@@ -273,7 +273,7 @@ static int tvmixer_clients(struct i2c_client *client)
unregister_sound_mixer(devices[i].minor);
devices[i].dev = NULL;
devices[i].minor = -1;
- printk("tvmixer: %s unregistered (#1)\n",client->name);
+ printk("tvmixer: %s unregistered (#1)\n",client->dev.name);
return 0;
}
}
@@ -298,13 +298,13 @@ static int tvmixer_clients(struct i2c_client *client)
if (0 != client->driver->command(client,VIDIOCGAUDIO,&va)) {
if (debug)
printk("tvmixer: %s: VIDIOCGAUDIO failed\n",
- client->name);
+ client->dev.name);
return -1;
}
if (0 == (va.flags & VIDEO_AUDIO_VOLUME)) {
if (debug)
printk("tvmixer: %s: has no volume control\n",
- client->name);
+ client->dev.name);
return -1;
}
@@ -318,7 +318,7 @@ static int tvmixer_clients(struct i2c_client *client)
devices[i].count = 0;
devices[i].dev = client;
printk("tvmixer: %s (%s) registered with minor %d\n",
- client->name,client->adapter->name,minor);
+ client->dev.name,client->adapter->dev.name,minor);
return 0;
}
@@ -344,7 +344,7 @@ static void tvmixer_cleanup_module(void)
if (devices[i].minor != -1) {
unregister_sound_mixer(devices[i].minor);
printk("tvmixer: %s unregistered (#2)\n",
- devices[i].dev->name);
+ devices[i].dev->dev.name);
}
}
}
diff --git a/include/linux/i2c-proc.h b/include/linux/i2c-sensor.h
index 618734230d46..0f33aaf297e3 100644
--- a/include/linux/i2c-proc.h
+++ b/include/linux/i2c-sensor.h
@@ -1,5 +1,5 @@
/*
- i2c-proc.h - Part of the i2c package
+ i2c-sensor.h - Part of the i2c package
was originally sensors.h - Part of lm_sensors, Linux kernel modules
for hardware monitoring
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
@@ -19,8 +19,8 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#ifndef _LINUX_I2C_PROC_H
-#define _LINUX_I2C_PROC_H
+#ifndef _LINUX_I2C_SENSOR_H
+#define _LINUX_I2C_SENSOR_H
#include <linux/sysctl.h>
@@ -34,46 +34,6 @@ typedef void (*i2c_real_callback) (struct i2c_client * client,
#define SENSORS_PROC_REAL_READ 2
#define SENSORS_PROC_REAL_WRITE 3
-/* These funcion reads or writes a 'real' value (encoded by the combination
- of an integer and a magnitude, the last is the power of ten the value
- should be divided with) to a /proc/sys directory. To use these functions,
- you must (before registering the ctl_table) set the extra2 field to the
- client, and the extra1 field to a function of the form:
- void func(struct i2c_client *client, int operation, int ctl_name,
- int *nrels_mag, long *results)
- This last function can be called for three values of operation. If
- operation equals SENSORS_PROC_REAL_INFO, the magnitude should be returned
- in nrels_mag. If operation equals SENSORS_PROC_REAL_READ, values should
- be read into results. nrels_mag should return the number of elements
- read; the maximum number is put in it on entry. Finally, if operation
- equals SENSORS_PROC_REAL_WRITE, the values in results should be
- written to the chip. nrels_mag contains on entry the number of elements
- found.
- In all cases, client points to the client we wish to interact with,
- and ctl_name is the SYSCTL id of the file we are accessing. */
-extern int i2c_sysctl_real(ctl_table * table, int *name, int nlen,
- void *oldval, size_t * oldlenp,
- void *newval, size_t newlen,
- void **context);
-extern int i2c_proc_real(ctl_table * ctl, int write, struct file *filp,
- void *buffer, size_t * lenp);
-
-
-
-/* These rather complex functions must be called when you want to add or
- delete an entry in /proc/sys/dev/sensors/chips (not yet implemented). It
- also creates a new directory within /proc/sys/dev/sensors/.
- ctl_template should be a template of the newly created directory. It is
- copied in memory. The extra2 field of each file is set to point to client.
- If any driver wants subdirectories within the newly created directory,
- these functions must be updated! */
-extern int i2c_register_entry(struct i2c_client *client,
- const char *prefix,
- ctl_table * ctl_template);
-
-extern void i2c_deregister_entry(int id);
-
-
/* A structure containing detect information.
Force variables overrule all other variables; they force a detection on
that place. If a specific chip is given, the module blindly assumes this
@@ -368,8 +328,7 @@ struct i2c_address_data {
SENSORS_INSMOD
typedef int i2c_found_addr_proc(struct i2c_adapter *adapter,
- int addr, unsigned short flags,
- int kind);
+ int addr, int kind);
/* Detect function. It iterates over all possible addresses itself. For
SMBus addresses, it will only call found_proc if some client is connected
@@ -410,5 +369,5 @@ struct i2c_chips_data {
char name[SENSORS_PREFIX_MAX + 13];
};
-#endif /* def _LINUX_I2C_PROC_H */
+#endif /* def _LINUX_I2C_SENSOR_H */
diff --git a/include/linux/i2c-vid.h b/include/linux/i2c-vid.h
new file mode 100644
index 000000000000..86ed1178193f
--- /dev/null
+++ b/include/linux/i2c-vid.h
@@ -0,0 +1,62 @@
+/*
+ i2c-vid.h - Part of lm_sensors, Linux kernel modules for hardware
+ monitoring
+ Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
+ With assistance from Trent Piepho <xyzzy@speakeasy.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ This file contains common code for decoding VID pins.
+ This file is #included in various chip drivers in this directory.
+ As the user is unlikely to load more than one driver which
+ includes this code we don't worry about the wasted space.
+ Reference: VRM x.y DC-DC Converter Design Guidelines,
+ available at http://developer.intel.com
+*/
+
+/*
+ Legal val values 00 - 1F.
+ vrm is the Intel VRM document version.
+ Note: vrm version is scaled by 10 and the return value is scaled by 1000
+ to avoid floating point in the kernel.
+*/
+
+#define DEFAULT_VRM 82
+
+static inline int vid_from_reg(int val, int vrm)
+{
+ switch(vrm) {
+
+ case 91: /* VRM 9.1 */
+ case 90: /* VRM 9.0 */
+ return(val == 0x1f ? 0 :
+ 1850 - val * 25);
+
+ case 85: /* VRM 8.5 */
+ return((val & 0x10 ? 25 : 0) +
+ ((val & 0x0f) > 0x04 ? 2050 : 1250) -
+ ((val & 0x0f) * 50));
+
+ case 84: /* VRM 8.4 */
+ val &= 0x0f;
+ /* fall through */
+ default: /* VRM 8.2 */
+ return(val == 0x1f ? 0 :
+ val & 0x10 ? 5100 - (val) * 100 :
+ 2050 - (val) * 50);
+ }
+}