Commit 37581a1c authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge kroah.com:/home/greg/linux/BK/bleed-2.5

into kroah.com:/home/greg/linux/BK/i2c-2.5
parents 797886bf 7eaa539b
...@@ -196,17 +196,6 @@ config I2C_CHARDEV ...@@ -196,17 +196,6 @@ config I2C_CHARDEV
<file:Documentation/modules.txt>. <file:Documentation/modules.txt>.
The module will be called i2c-dev. 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/busses/Kconfig
source drivers/i2c/chips/Kconfig source drivers/i2c/chips/Kconfig
......
...@@ -14,5 +14,5 @@ obj-$(CONFIG_ITE_I2C_ALGO) += i2c-algo-ite.o ...@@ -14,5 +14,5 @@ obj-$(CONFIG_ITE_I2C_ALGO) += i2c-algo-ite.o
obj-$(CONFIG_ITE_I2C_ADAP) += i2c-adap-ite.o obj-$(CONFIG_ITE_I2C_ADAP) += i2c-adap-ite.o
obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o
obj-$(CONFIG_SCx200_ACB) += scx200_acb.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/ obj-y += busses/ chips/
# #
# Sensor device configuration # Sensor device configuration
# All depend on EXPERIMENTAL, I2C and I2C_PROC. # All depend on EXPERIMENTAL and I2C
# #
menu "I2C Hardware Sensors Chip support" menu "I2C Hardware Sensors Chip support"
config SENSORS_ADM1021 config SENSORS_ADM1021
tristate " Analog Devices ADM1021 and compatibles" tristate " Analog Devices ADM1021 and compatibles"
depends on I2C && I2C_PROC depends on I2C && EXPERIMENTAL
help help
If you say yes here you get support for Analog Devices ADM1021 If you say yes here you get support for Analog Devices ADM1021
and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A, and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A,
...@@ -24,7 +24,7 @@ config SENSORS_ADM1021 ...@@ -24,7 +24,7 @@ config SENSORS_ADM1021
config SENSORS_LM75 config SENSORS_LM75
tristate " National Semiconductors LM75 and compatibles" tristate " National Semiconductors LM75 and compatibles"
depends on I2C && I2C_PROC depends on I2C && EXPERIMENTAL
help help
If you say yes here you get support for National Semiconductor LM75 If you say yes here you get support for National Semiconductor LM75
sensor chips and clones: Dallas Semi DS75 and DS1775, TelCon sensor chips and clones: Dallas Semi DS75 and DS1775, TelCon
...@@ -36,5 +36,37 @@ config SENSORS_LM75 ...@@ -36,5 +36,37 @@ config SENSORS_LM75
You will also need the latest user-space utilties: you can find them You will also need the latest user-space utilties: you can find them
in the lm_sensors package, which you can download at in the lm_sensors package, which you can download at
http://www.lm-sensors.nu 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 endmenu
...@@ -4,3 +4,5 @@ ...@@ -4,3 +4,5 @@
obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o
obj-$(CONFIG_SENSORS_LM75) += lm75.o obj-$(CONFIG_SENSORS_LM75) += lm75.o
obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/i2c-proc.h> #include <linux/i2c-sensor.h>
/* Registers */ /* Registers */
...@@ -53,34 +53,34 @@ SENSORS_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1 ...@@ -53,34 +53,34 @@ SENSORS_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1
/* The adm1021 registers */ /* The adm1021 registers */
/* Read-only */ /* Read-only */
#define ADM1021_REG_TEMP 0x00 #define ADM1021_REG_TEMP 0x00
#define ADM1021_REG_REMOTE_TEMP 0x01 #define ADM1021_REG_REMOTE_TEMP 0x01
#define ADM1021_REG_STATUS 0x02 #define ADM1021_REG_STATUS 0x02
#define ADM1021_REG_MAN_ID 0x0FE /* 0x41 = AMD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi*/ #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_DEV_ID 0x0FF /* ADM1021 = 0x0X, ADM1023 = 0x3X */
#define ADM1021_REG_DIE_CODE 0x0FF /* MAX1617A */ #define ADM1021_REG_DIE_CODE 0x0FF /* MAX1617A */
/* These use different addresses for reading/writing */ /* These use different addresses for reading/writing */
#define ADM1021_REG_CONFIG_R 0x03 #define ADM1021_REG_CONFIG_R 0x03
#define ADM1021_REG_CONFIG_W 0x09 #define ADM1021_REG_CONFIG_W 0x09
#define ADM1021_REG_CONV_RATE_R 0x04 #define ADM1021_REG_CONV_RATE_R 0x04
#define ADM1021_REG_CONV_RATE_W 0x0A #define ADM1021_REG_CONV_RATE_W 0x0A
/* These are for the ADM1023's additional precision on the remote temp sensor */ /* These are for the ADM1023's additional precision on the remote temp sensor */
#define ADM1021_REG_REM_TEMP_PREC 0x010 #define ADM1021_REG_REM_TEMP_PREC 0x010
#define ADM1021_REG_REM_OFFSET 0x011 #define ADM1021_REG_REM_OFFSET 0x011
#define ADM1021_REG_REM_OFFSET_PREC 0x012 #define ADM1021_REG_REM_OFFSET_PREC 0x012
#define ADM1021_REG_REM_TOS_PREC 0x013 #define ADM1021_REG_REM_TOS_PREC 0x013
#define ADM1021_REG_REM_THYST_PREC 0x014 #define ADM1021_REG_REM_THYST_PREC 0x014
/* limits */ /* limits */
#define ADM1021_REG_TOS_R 0x05 #define ADM1021_REG_TOS_R 0x05
#define ADM1021_REG_TOS_W 0x0B #define ADM1021_REG_TOS_W 0x0B
#define ADM1021_REG_REMOTE_TOS_R 0x07 #define ADM1021_REG_REMOTE_TOS_R 0x07
#define ADM1021_REG_REMOTE_TOS_W 0x0D #define ADM1021_REG_REMOTE_TOS_W 0x0D
#define ADM1021_REG_THYST_R 0x06 #define ADM1021_REG_THYST_R 0x06
#define ADM1021_REG_THYST_W 0x0C #define ADM1021_REG_THYST_W 0x0C
#define ADM1021_REG_REMOTE_THYST_R 0x08 #define ADM1021_REG_REMOTE_THYST_R 0x08
#define ADM1021_REG_REMOTE_THYST_W 0x0E #define ADM1021_REG_REMOTE_THYST_W 0x0E
/* write-only */ /* write-only */
#define ADM1021_REG_ONESHOT 0x0F #define ADM1021_REG_ONESHOT 0x0F
/* Conversions. Rounding and limit checking is only done on the TO_REG /* 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 ...@@ -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. these macros are called: arguments may be evaluated more than once.
Fixing this is just not worth it. */ Fixing this is just not worth it. */
/* Conversions note: 1021 uses normal integer signed-byte format*/ /* Conversions note: 1021 uses normal integer signed-byte format*/
#define TEMP_FROM_REG(val) (val > 127 ? val-256 : val) #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_TO_REG(val) (SENSORS_LIMIT((val < 0 ? val+256 : val),0,255))
/* Initial values */ /* Initial values */
...@@ -97,44 +97,43 @@ SENSORS_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1 ...@@ -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., 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 a lower temp than THYST actually triggers an alarm instead of
clearing it. Weird, ey? --Phil */ clearing it. Weird, ey? --Phil */
#define adm1021_INIT_TOS 60 #define adm1021_INIT_TOS 60
#define adm1021_INIT_THYST 20 #define adm1021_INIT_THYST 20
#define adm1021_INIT_REMOTE_TOS 60 #define adm1021_INIT_REMOTE_TOS 60
#define adm1021_INIT_REMOTE_THYST 20 #define adm1021_INIT_REMOTE_THYST 20
/* Each client has this additional data */ /* Each client has this additional data */
struct adm1021_data { struct adm1021_data {
int sysctl_id;
enum chips type; enum chips type;
struct semaphore update_lock; struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */ char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */ unsigned long last_updated; /* In jiffies */
u8 temp, temp_os, temp_hyst; /* Register values */ u8 temp_max; /* Register values */
u8 remote_temp, remote_temp_os, remote_temp_hyst, alarms, die_code; 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 */ /* Special values for ADM1023 only */
u8 remote_temp_prec, remote_temp_os_prec, remote_temp_hyst_prec, u8 remote_temp_prec;
remote_temp_offset, remote_temp_offset_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_attach_adapter(struct i2c_adapter *adapter);
static int adm1021_detect(struct i2c_adapter *adapter, int address, static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind);
unsigned short flags, int kind);
static void adm1021_init_client(struct i2c_client *client); static void adm1021_init_client(struct i2c_client *client);
static int adm1021_detach_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_read_value(struct i2c_client *client, u8 reg);
static int adm1021_write_value(struct i2c_client *client, u8 reg, static int adm1021_write_value(struct i2c_client *client, u8 reg,
u16 value); 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); static void adm1021_update_client(struct i2c_client *client);
/* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */ /* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */
...@@ -151,45 +150,63 @@ static struct i2c_driver adm1021_driver = { ...@@ -151,45 +150,63 @@ static struct i2c_driver adm1021_driver = {
.detach_client = adm1021_detach_client, .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 /* I choose here for semi-static allocation. Complete dynamic
allocation could also be used; the code needed for this would probably allocation could also be used; the code needed for this would probably
take more memory than the datastructure takes now. */ take more memory than the datastructure takes now. */
static int adm1021_id = 0; 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) static int adm1021_attach_adapter(struct i2c_adapter *adapter)
{ {
return i2c_detect(adapter, &addr_data, adm1021_detect); return i2c_detect(adapter, &addr_data, adm1021_detect);
} }
static int adm1021_detect(struct i2c_adapter *adapter, int address, static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind)
unsigned short flags, int kind)
{ {
int i; int i;
struct i2c_client *new_client; struct i2c_client *new_client;
...@@ -202,8 +219,7 @@ static int adm1021_detect(struct i2c_adapter *adapter, int address, ...@@ -202,8 +219,7 @@ static int adm1021_detect(struct i2c_adapter *adapter, int address,
at this moment; i2c_detect really won't call us. */ at this moment; i2c_detect really won't call us. */
#ifdef DEBUG #ifdef DEBUG
if (i2c_is_isa_adapter(adapter)) { if (i2c_is_isa_adapter(adapter)) {
printk dev_dbg(&adapter->dev, "adm1021_detect called for an ISA bus adapter?!?\n");
("adm1021.o: adm1021_detect called for an ISA bus adapter?!?\n");
return 0; return 0;
} }
#endif #endif
...@@ -232,35 +248,28 @@ static int adm1021_detect(struct i2c_adapter *adapter, int address, ...@@ -232,35 +248,28 @@ static int adm1021_detect(struct i2c_adapter *adapter, int address,
new_client->flags = 0; new_client->flags = 0;
/* Now, we do the remaining detection. */ /* Now, we do the remaining detection. */
if (kind < 0) { if (kind < 0) {
if ( if ((adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0x03) != 0x00)
(adm1021_read_value(new_client, ADM1021_REG_STATUS) &
0x03) != 0x00)
goto error1; goto error1;
} }
/* Determine the chip type. */ /* Determine the chip type. */
if (kind <= 0) { if (kind <= 0) {
i = adm1021_read_value(new_client, ADM1021_REG_MAN_ID); i = adm1021_read_value(new_client, ADM1021_REG_MAN_ID);
if (i == 0x41) if (i == 0x41)
if ((adm1021_read_value (new_client, ADM1021_REG_DEV_ID) & 0x0F0) == 0x030) if ((adm1021_read_value(new_client, ADM1021_REG_DEV_ID) & 0x0F0) == 0x030)
kind = adm1023; kind = adm1023;
else else
kind = adm1021; kind = adm1021;
else if (i == 0x49) else if (i == 0x49)
kind = thmc10; kind = thmc10;
else if (i == 0x23) else if (i == 0x23)
kind = gl523sm; kind = gl523sm;
else if ((i == 0x4d) && else if ((i == 0x4d) &&
(adm1021_read_value (adm1021_read_value(new_client, ADM1021_REG_DEV_ID) == 0x01))
(new_client, ADM1021_REG_DEV_ID) == 0x01))
kind = max1617a; kind = max1617a;
/* LM84 Mfr ID in a different place */ /* LM84 Mfr ID in a different place */
else else if (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) == 0x00)
if (adm1021_read_value
(new_client, ADM1021_REG_CONV_RATE_R) == 0x00)
kind = lm84; kind = lm84;
else if (i == 0x54) else if (i == 0x54)
kind = mc1066; kind = mc1066;
...@@ -293,10 +302,8 @@ static int adm1021_detect(struct i2c_adapter *adapter, int address, ...@@ -293,10 +302,8 @@ static int adm1021_detect(struct i2c_adapter *adapter, int address,
type_name = "mc1066"; type_name = "mc1066";
client_name = "MC1066 chip"; client_name = "MC1066 chip";
} else { } else {
#ifdef DEBUG dev_err(&adapter->dev, "Internal error: unknown kind (%d)?!?",
printk("adm1021.o: Internal error: unknown kind (%d)?!?", kind);
kind);
#endif
goto error1; goto error1;
} }
...@@ -312,25 +319,24 @@ static int adm1021_detect(struct i2c_adapter *adapter, int address, ...@@ -312,25 +319,24 @@ static int adm1021_detect(struct i2c_adapter *adapter, int address,
if ((err = i2c_attach_client(new_client))) if ((err = i2c_attach_client(new_client)))
goto error3; goto error3;
/* Register a new directory entry with module sensors */ device_create_file(&new_client->dev, &dev_attr_temp_max1);
err = i2c_register_entry(new_client, type_name, device_create_file(&new_client->dev, &dev_attr_temp_min1);
(data->type == adm1021) ? device_create_file(&new_client->dev, &dev_attr_temp_input1);
adm1021_dir_table_template : device_create_file(&new_client->dev, &dev_attr_temp_max2);
adm1021_max_dir_table_template); device_create_file(&new_client->dev, &dev_attr_temp_min2);
if (err < 0) device_create_file(&new_client->dev, &dev_attr_temp_input2);
goto error4; 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 */ /* Initialize the ADM1021 chip */
adm1021_init_client(new_client); adm1021_init_client(new_client);
return 0; return 0;
error4: error3:
i2c_detach_client(new_client); error1:
error3:
error1:
kfree(new_client); kfree(new_client);
error0: error0:
return err; return err;
} }
...@@ -353,21 +359,15 @@ static void adm1021_init_client(struct i2c_client *client) ...@@ -353,21 +359,15 @@ static void adm1021_init_client(struct i2c_client *client)
static int adm1021_detach_client(struct i2c_client *client) static int adm1021_detach_client(struct i2c_client *client)
{ {
int err; int err;
i2c_deregister_entry(((struct adm1021_data *) (i2c_get_clientdata(client)))->sysctl_id);
if ((err = i2c_detach_client(client))) { if ((err = i2c_detach_client(client))) {
printk dev_err(&client->dev, "Client deregistration failed, client not detached.\n");
("adm1021.o: Client deregistration failed, client not detached.\n");
return err; return err;
} }
kfree(client); kfree(client);
return 0; return 0;
} }
/* All registers are byte-sized */ /* All registers are byte-sized */
...@@ -391,39 +391,23 @@ static void adm1021_update_client(struct i2c_client *client) ...@@ -391,39 +391,23 @@ static void adm1021_update_client(struct i2c_client *client)
if ((jiffies - data->last_updated > HZ + HZ / 2) || if ((jiffies - data->last_updated > HZ + HZ / 2) ||
(jiffies < data->last_updated) || !data->valid) { (jiffies < data->last_updated) || !data->valid) {
dev_dbg(&client->dev, "Starting adm1021 update\n");
#ifdef DEBUG
printk("Starting adm1021 update\n"); data->temp_input = adm1021_read_value(client, ADM1021_REG_TEMP);
#endif data->temp_max = adm1021_read_value(client, ADM1021_REG_TOS_R);
data->temp_hyst = adm1021_read_value(client, ADM1021_REG_THYST_R);
data->temp = adm1021_read_value(client, ADM1021_REG_TEMP); data->remote_temp_input = adm1021_read_value(client, ADM1021_REG_REMOTE_TEMP);
data->temp_os = data->remote_temp_max = adm1021_read_value(client, ADM1021_REG_REMOTE_TOS_R);
adm1021_read_value(client, ADM1021_REG_TOS_R); data->remote_temp_hyst = adm1021_read_value(client, ADM1021_REG_REMOTE_THYST_R);
data->temp_hyst = data->alarms = adm1021_read_value(client, ADM1021_REG_STATUS) & 0xec;
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;
if (data->type == adm1021) if (data->type == adm1021)
data->die_code = data->die_code = adm1021_read_value(client, ADM1021_REG_DIE_CODE);
adm1021_read_value(client,
ADM1021_REG_DIE_CODE);
if (data->type == adm1023) { if (data->type == adm1023) {
data->remote_temp_prec = data->remote_temp_prec = adm1021_read_value(client, ADM1021_REG_REM_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_os_prec = data->remote_temp_hyst_prec = adm1021_read_value(client, ADM1021_REG_REM_THYST_PREC);
adm1021_read_value(client, ADM1021_REG_REM_TOS_PREC); data->remote_temp_offset = adm1021_read_value(client, ADM1021_REG_REM_OFFSET);
data->remote_temp_hyst_prec = data->remote_temp_offset_prec = adm1021_read_value(client, ADM1021_REG_REM_OFFSET_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->last_updated = jiffies;
data->valid = 1; data->valid = 1;
...@@ -433,6 +417,9 @@ static void adm1021_update_client(struct i2c_client *client) ...@@ -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, static void adm1021_temp(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results) int ctl_name, int *nrels_mag, long *results)
{ {
...@@ -442,15 +429,15 @@ static void adm1021_temp(struct i2c_client *client, int operation, ...@@ -442,15 +429,15 @@ static void adm1021_temp(struct i2c_client *client, int operation,
*nrels_mag = 0; *nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) { else if (operation == SENSORS_PROC_REAL_READ) {
adm1021_update_client(client); 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[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; *nrels_mag = 3;
} else if (operation == SENSORS_PROC_REAL_WRITE) { } else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) { 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, adm1021_write_value(client, ADM1021_REG_TOS_W,
data->temp_os); data->temp_max);
} }
if (*nrels_mag >= 2) { if (*nrels_mag >= 2) {
data->temp_hyst = TEMP_TO_REG(results[1]); data->temp_hyst = TEMP_TO_REG(results[1]);
...@@ -460,6 +447,7 @@ static void adm1021_temp(struct i2c_client *client, int operation, ...@@ -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, static void adm1021_remote_temp(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results) int ctl_name, int *nrels_mag, long *results)
{ {
...@@ -471,68 +459,53 @@ static void adm1021_remote_temp(struct i2c_client *client, int operation, ...@@ -471,68 +459,53 @@ static void adm1021_remote_temp(struct i2c_client *client, int operation,
else { *nrels_mag = 0; } else { *nrels_mag = 0; }
else if (operation == SENSORS_PROC_REAL_READ) { else if (operation == SENSORS_PROC_REAL_READ) {
adm1021_update_client(client); 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[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) { if (data->type == adm1023) {
results[0]=results[0]*1000 + results[0] = results[0]*1000 + ((data->remote_temp_os_prec >> 5) * 125);
((data->remote_temp_os_prec >> 5) * 125); results[1] = results[1]*1000 + ((data->remote_temp_hyst_prec >> 5) * 125);
results[1]=results[1]*1000 + results[2] = (TEMP_FROM_REG(data->remote_temp_offset)*1000) + ((data->remote_temp_offset_prec >> 5) * 125);
((data->remote_temp_hyst_prec >> 5) * 125); results[3] = (TEMP_FROM_REG(data->remote_temp_input)*1000) + ((data->remote_temp_prec >> 5) * 125);
results[2]=(TEMP_FROM_REG(data->remote_temp_offset)*1000) + *nrels_mag = 4;
((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;
} else { } else {
*nrels_mag = 3; *nrels_mag = 3;
} }
} else if (operation == SENSORS_PROC_REAL_WRITE) { } else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) { if (*nrels_mag >= 1) {
if (data->type == adm1023) { if (data->type == adm1023) {
prec=((results[0]-((results[0]/1000)*1000))/125)<<5; prec = ((results[0]-((results[0]/1000)*1000))/125)<<5;
adm1021_write_value(client, adm1021_write_value(client, ADM1021_REG_REM_TOS_PREC, prec);
ADM1021_REG_REM_TOS_PREC, results[0] = results[0]/1000;
prec); data->remote_temp_os_prec=prec;
results[0]=results[0]/1000;
data->remote_temp_os_prec=prec;
} }
data->remote_temp_os = TEMP_TO_REG(results[0]); data->remote_temp_max = TEMP_TO_REG(results[0]);
adm1021_write_value(client, adm1021_write_value(client, ADM1021_REG_REMOTE_TOS_W, data->remote_temp_max);
ADM1021_REG_REMOTE_TOS_W,
data->remote_temp_os);
} }
if (*nrels_mag >= 2) { if (*nrels_mag >= 2) {
if (data->type == adm1023) { if (data->type == adm1023) {
prec=((results[1]-((results[1]/1000)*1000))/125)<<5; prec = ((results[1]-((results[1]/1000)*1000))/125)<<5;
adm1021_write_value(client, adm1021_write_value(client, ADM1021_REG_REM_THYST_PREC, prec);
ADM1021_REG_REM_THYST_PREC, results[1] = results[1]/1000;
prec); data->remote_temp_hyst_prec=prec;
results[1]=results[1]/1000;
data->remote_temp_hyst_prec=prec;
} }
data->remote_temp_hyst = TEMP_TO_REG(results[1]); data->remote_temp_hyst = TEMP_TO_REG(results[1]);
adm1021_write_value(client, adm1021_write_value(client, ADM1021_REG_REMOTE_THYST_W, data->remote_temp_hyst);
ADM1021_REG_REMOTE_THYST_W,
data->remote_temp_hyst);
} }
if (*nrels_mag >= 3) { if (*nrels_mag >= 3) {
if (data->type == adm1023) { if (data->type == adm1023) {
prec=((results[2]-((results[2]/1000)*1000))/125)<<5; prec = ((results[2]-((results[2]/1000)*1000))/125)<<5;
adm1021_write_value(client, adm1021_write_value(client, ADM1021_REG_REM_OFFSET_PREC, prec);
ADM1021_REG_REM_OFFSET_PREC, results[2]=results[2]/1000;
prec); data->remote_temp_offset_prec=prec;
results[2]=results[2]/1000; data->remote_temp_offset=results[2];
data->remote_temp_offset_prec=prec; adm1021_write_value(client, ADM1021_REG_REM_OFFSET, data->remote_temp_offset);
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, static void adm1021_die_code(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results) int ctl_name, int *nrels_mag, long *results)
{ {
...@@ -549,6 +522,7 @@ static void adm1021_die_code(struct i2c_client *client, int operation, ...@@ -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, static void adm1021_alarms(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results) int ctl_name, int *nrels_mag, long *results)
{ {
...@@ -574,8 +548,8 @@ static void __exit sensors_adm1021_exit(void) ...@@ -574,8 +548,8 @@ static void __exit sensors_adm1021_exit(void)
i2c_del_driver(&adm1021_driver); i2c_del_driver(&adm1021_driver);
} }
MODULE_AUTHOR MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl> and "
("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>"); "Philip Edelbrock <phil@netroedge.com>");
MODULE_DESCRIPTION("adm1021 driver"); MODULE_DESCRIPTION("adm1021 driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
......
...@@ -18,14 +18,14 @@ ...@@ -18,14 +18,14 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
/* #define DEBUG 1 */
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/i2c.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 */ /* Addresses to scan */
static unsigned short normal_i2c[] = { SENSORS_I2C_END }; static unsigned short normal_i2c[] = { SENSORS_I2C_END };
...@@ -39,97 +39,113 @@ SENSORS_INSMOD_1(lm75); ...@@ -39,97 +39,113 @@ SENSORS_INSMOD_1(lm75);
/* Many LM75 constants specified below */ /* Many LM75 constants specified below */
/* The LM75 registers */ /* The LM75 registers */
#define LM75_REG_TEMP 0x00 #define LM75_REG_TEMP 0x00
#define LM75_REG_CONF 0x01 #define LM75_REG_CONF 0x01
#define LM75_REG_TEMP_HYST 0x02 #define LM75_REG_TEMP_HYST 0x02
#define LM75_REG_TEMP_OS 0x03 #define LM75_REG_TEMP_OS 0x03
/* Conversions. Rounding and limit checking is only done on the TO_REG /* Conversions. Rounding and limit checking is only done on the TO_REG
variants. Note that you should be a bit careful with which arguments variants. Note that you should be a bit careful with which arguments
these macros are called: arguments may be evaluated more than once. these macros are called: arguments may be evaluated more than once.
Fixing this is just not worth it. */ Fixing this is just not worth it. */
#define TEMP_FROM_REG(val) ((((val & 0x7fff) >> 7) * 5) | ((val & 0x8000)?-256:0)) #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_TO_REG(val) (SENSORS_LIMIT((val<0?(0x200+((val)/5))<<7:(((val) + 2) / 5) << 7),0,0xffff))
/* Initial values */ /* Initial values */
#define LM75_INIT_TEMP_OS 600 #define LM75_INIT_TEMP_OS 600
#define LM75_INIT_TEMP_HYST 500 #define LM75_INIT_TEMP_HYST 500
/* Each client has this additional data */ /* Each client has this additional data */
struct lm75_data { struct lm75_data {
int sysctl_id; struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
struct semaphore update_lock; unsigned long last_updated; /* In jiffies */
char valid; /* !=0 if following fields are valid */ u16 temp_input; /* Register values */
unsigned long last_updated; /* In jiffies */ u16 temp_max;
u16 temp_hyst;
u16 temp, temp_os, temp_hyst; /* Register values */
}; };
static int lm75_attach_adapter(struct i2c_adapter *adapter); static int lm75_attach_adapter(struct i2c_adapter *adapter);
static int lm75_detect(struct i2c_adapter *adapter, int address, static int lm75_detect(struct i2c_adapter *adapter, int address, int kind);
unsigned short flags, int kind);
static void lm75_init_client(struct i2c_client *client); static void lm75_init_client(struct i2c_client *client);
static int lm75_detach_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_read_value(struct i2c_client *client, u8 reg);
static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value); 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); static void lm75_update_client(struct i2c_client *client);
/* This is the driver that will be inserted */ /* This is the driver that will be inserted */
static struct i2c_driver lm75_driver = { static struct i2c_driver lm75_driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "LM75 sensor", .name = "lm75",
.id = I2C_DRIVERID_LM75, .id = I2C_DRIVERID_LM75,
.flags = I2C_DF_NOTIFY, .flags = I2C_DF_NOTIFY,
.attach_adapter = lm75_attach_adapter, .attach_adapter = lm75_attach_adapter,
.detach_client = lm75_detach_client, .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; 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) static int lm75_attach_adapter(struct i2c_adapter *adapter)
{ {
return i2c_detect(adapter, &addr_data, lm75_detect); return i2c_detect(adapter, &addr_data, lm75_detect);
} }
/* This function is called by i2c_detect */ /* This function is called by i2c_detect */
static int lm75_detect(struct i2c_adapter *adapter, int address, static int lm75_detect(struct i2c_adapter *adapter, int address, int kind)
unsigned short flags, int kind)
{ {
int i, cur, conf, hyst, os; int i, cur, conf, hyst, os;
struct i2c_client *new_client; struct i2c_client *new_client;
struct lm75_data *data; struct lm75_data *data;
int err = 0; 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 /* 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. */ at this moment; i2c_detect really won't call us. */
#ifdef DEBUG #ifdef DEBUG
if (i2c_is_isa_adapter(adapter)) { if (i2c_is_isa_adapter(adapter)) {
printk dev_dbg(&adapter->dev,
("lm75.o: lm75_detect called for an ISA bus adapter?!?\n"); "lm75_detect called for an ISA bus adapter?!?\n");
return 0; goto exit;
} }
#endif #endif
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA)) I2C_FUNC_SMBUS_WORD_DATA))
goto error0; goto exit;
/* OK. For now, we presume we have a valid client. We now create the /* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet. client structure, even though we cannot fill it completely yet.
...@@ -138,7 +154,7 @@ static int lm75_detect(struct i2c_adapter *adapter, int address, ...@@ -138,7 +154,7 @@ static int lm75_detect(struct i2c_adapter *adapter, int address,
sizeof(struct lm75_data), sizeof(struct lm75_data),
GFP_KERNEL))) { GFP_KERNEL))) {
err = -ENOMEM; err = -ENOMEM;
goto error0; goto exit;
} }
memset(new_client, 0x00, sizeof(struct i2c_client) + memset(new_client, 0x00, sizeof(struct i2c_client) +
sizeof(struct lm75_data)); sizeof(struct lm75_data));
...@@ -157,16 +173,10 @@ static int lm75_detect(struct i2c_adapter *adapter, int address, ...@@ -157,16 +173,10 @@ static int lm75_detect(struct i2c_adapter *adapter, int address,
hyst = i2c_smbus_read_word_data(new_client, 2); hyst = i2c_smbus_read_word_data(new_client, 2);
os = i2c_smbus_read_word_data(new_client, 3); os = i2c_smbus_read_word_data(new_client, 3);
for (i = 0; i <= 0x1f; i++) for (i = 0; i <= 0x1f; i++)
if ( if ((i2c_smbus_read_byte_data(new_client, i * 8 + 1) != conf) ||
(i2c_smbus_read_byte_data (i2c_smbus_read_word_data(new_client, i * 8 + 2) != hyst) ||
(new_client, i * 8 + 1) != conf) (i2c_smbus_read_word_data(new_client, i * 8 + 3) != os))
|| goto exit_free;
(i2c_smbus_read_word_data
(new_client, i * 8 + 2) != hyst)
||
(i2c_smbus_read_word_data
(new_client, i * 8 + 3) != os))
goto error1;
} }
/* Determine the chip type - only one kind supported! */ /* Determine the chip type - only one kind supported! */
...@@ -174,15 +184,15 @@ static int lm75_detect(struct i2c_adapter *adapter, int address, ...@@ -174,15 +184,15 @@ static int lm75_detect(struct i2c_adapter *adapter, int address,
kind = lm75; kind = lm75;
if (kind == lm75) { if (kind == lm75) {
type_name = "lm75"; name = "lm75";
client_name = "LM75 chip";
} else { } else {
pr_debug("lm75.o: Internal error: unknown kind (%d)?!?", kind); dev_dbg(&adapter->dev, "Internal error: unknown kind (%d)?!?",
goto error1; kind);
goto exit_free;
} }
/* Fill in the remaining client fields and put it into the global list */ /* 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++; new_client->id = lm75_id++;
data->valid = 0; data->valid = 0;
...@@ -190,36 +200,23 @@ static int lm75_detect(struct i2c_adapter *adapter, int address, ...@@ -190,36 +200,23 @@ static int lm75_detect(struct i2c_adapter *adapter, int address,
/* Tell the I2C layer a new client has arrived */ /* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client))) if ((err = i2c_attach_client(new_client)))
goto error3; goto exit_free;
/* Register a new directory entry with module sensors */ device_create_file(&new_client->dev, &dev_attr_temp_max);
i = i2c_register_entry(new_client, type_name, lm75_dir_table_template); device_create_file(&new_client->dev, &dev_attr_temp_min);
if (i < 0) { device_create_file(&new_client->dev, &dev_attr_temp_input);
err = i;
goto error4;
}
data->sysctl_id = i;
lm75_init_client(new_client); lm75_init_client(new_client);
return 0; return 0;
/* OK, this is not exactly good programming practice, usually. But it is exit_free:
very code-efficient in this case. */
error4:
i2c_detach_client(new_client);
error3:
error1:
kfree(new_client); kfree(new_client);
error0: exit:
return err; return err;
} }
static int lm75_detach_client(struct i2c_client *client) 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); i2c_detach_client(client);
kfree(client); kfree(client);
return 0; return 0;
...@@ -271,12 +268,11 @@ static void lm75_update_client(struct i2c_client *client) ...@@ -271,12 +268,11 @@ static void lm75_update_client(struct i2c_client *client)
if ((jiffies - data->last_updated > HZ + HZ / 2) || if ((jiffies - data->last_updated > HZ + HZ / 2) ||
(jiffies < data->last_updated) || !data->valid) { (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_input = lm75_read_value(client, LM75_REG_TEMP);
data->temp_os = lm75_read_value(client, LM75_REG_TEMP_OS); data->temp_max = lm75_read_value(client, LM75_REG_TEMP_OS);
data->temp_hyst = data->temp_hyst = lm75_read_value(client, LM75_REG_TEMP_HYST);
lm75_read_value(client, LM75_REG_TEMP_HYST);
data->last_updated = jiffies; data->last_updated = jiffies;
data->valid = 1; data->valid = 1;
} }
...@@ -284,33 +280,6 @@ static void lm75_update_client(struct i2c_client *client) ...@@ -284,33 +280,6 @@ static void lm75_update_client(struct i2c_client *client)
up(&data->update_lock); 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) static int __init sensors_lm75_init(void)
{ {
return i2c_add_driver(&lm75_driver); return i2c_add_driver(&lm75_driver);
......
/*
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);
/*
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);
...@@ -29,7 +29,6 @@ ...@@ -29,7 +29,6 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
...@@ -46,15 +45,6 @@ static DECLARE_MUTEX(core_lists); ...@@ -46,15 +45,6 @@ static DECLARE_MUTEX(core_lists);
/**** debug level */ /**** debug level */
static int i2c_debug; 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) int i2c_device_probe(struct device *dev)
{ {
return -ENODEV; return -ENODEV;
...@@ -98,10 +88,6 @@ int i2c_add_adapter(struct i2c_adapter *adap) ...@@ -98,10 +88,6 @@ int i2c_add_adapter(struct i2c_adapter *adap)
goto out_unlock; goto out_unlock;
} }
res = i2cproc_register(adap, i);
if (res)
goto out_unlock;
adapters[i] = adap; adapters[i] = adap;
init_MUTEX(&adap->bus); init_MUTEX(&adap->bus);
...@@ -180,8 +166,6 @@ int i2c_del_adapter(struct i2c_adapter *adap) ...@@ -180,8 +166,6 @@ int i2c_del_adapter(struct i2c_adapter *adap)
} }
} }
i2cproc_remove(i);
/* clean up the sysfs representation */ /* clean up the sysfs representation */
device_unregister(&adap->dev); device_unregister(&adap->dev);
...@@ -392,7 +376,8 @@ int i2c_attach_client(struct i2c_client *client) ...@@ -392,7 +376,8 @@ int i2c_attach_client(struct i2c_client *client)
client->dev.driver = &client->driver->driver; client->dev.driver = &client->driver->driver;
client->dev.bus = &i2c_bus_type; 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); printk("registering %s\n", client->dev.bus_id);
device_register(&client->dev); device_register(&client->dev);
...@@ -494,173 +479,6 @@ int i2c_release_client(struct i2c_client *client) ...@@ -494,173 +479,6 @@ int i2c_release_client(struct i2c_client *client)
return 0; 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 */ /* 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) static int i2c_device_match(struct device *dev, struct device_driver *drv)
{ {
...@@ -675,13 +493,11 @@ struct bus_type i2c_bus_type = { ...@@ -675,13 +493,11 @@ struct bus_type i2c_bus_type = {
static int __init i2c_init(void) static int __init i2c_init(void)
{ {
bus_register(&i2c_bus_type); return bus_register(&i2c_bus_type);
return i2cproc_init();
} }
static void __exit i2c_exit(void) static void __exit i2c_exit(void)
{ {
i2cproc_cleanup();
bus_unregister(&i2c_bus_type); bus_unregister(&i2c_bus_type);
} }
......
/*
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);
/*
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);
...@@ -231,7 +231,7 @@ int adv717x_probe(struct i2c_adapter *adap) ...@@ -231,7 +231,7 @@ int adv717x_probe(struct i2c_adapter *adap)
static int adv717x_detach(struct i2c_client *client) static int adv717x_detach(struct i2c_client *client)
{ {
i2c_detach_client(client); i2c_detach_client(client);
i2c_get_clientdata(client); kfree(i2c_get_clientdata(client));
kfree(client); kfree(client);
return 0; return 0;
} }
......
...@@ -87,7 +87,7 @@ static int tvmixer_ioctl(struct inode *inode, struct file *file, unsigned int cm ...@@ -87,7 +87,7 @@ static int tvmixer_ioctl(struct inode *inode, struct file *file, unsigned int cm
if (cmd == SOUND_MIXER_INFO) { if (cmd == SOUND_MIXER_INFO) {
mixer_info info; mixer_info info;
strncpy(info.id, "tv card", sizeof(info.id)); 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 */; info.modify_counter = 42 /* FIXME */;
if (copy_to_user((void *)arg, &info, sizeof(info))) if (copy_to_user((void *)arg, &info, sizeof(info)))
return -EFAULT; return -EFAULT;
...@@ -96,7 +96,7 @@ static int tvmixer_ioctl(struct inode *inode, struct file *file, unsigned int cm ...@@ -96,7 +96,7 @@ static int tvmixer_ioctl(struct inode *inode, struct file *file, unsigned int cm
if (cmd == SOUND_OLD_MIXER_INFO) { if (cmd == SOUND_OLD_MIXER_INFO) {
_old_mixer_info info; _old_mixer_info info;
strncpy(info.id, "tv card", sizeof(info.id)); 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))) if (copy_to_user((void *)arg, &info, sizeof(info)))
return -EFAULT; return -EFAULT;
return 0; return 0;
...@@ -237,7 +237,7 @@ static int tvmixer_adapters(struct i2c_adapter *adap) ...@@ -237,7 +237,7 @@ static int tvmixer_adapters(struct i2c_adapter *adap)
int i; int i;
if (debug) 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++) { for (i=0; i<I2C_CLIENT_MAX; i++) {
if (!adap->clients[i]) if (!adap->clients[i])
continue; continue;
...@@ -261,10 +261,10 @@ static int tvmixer_clients(struct i2c_client *client) ...@@ -261,10 +261,10 @@ static int tvmixer_clients(struct i2c_client *client)
/* ignore that one */ /* ignore that one */
if (debug) if (debug)
printk("tvmixer: %s is not a tv card\n", printk("tvmixer: %s is not a tv card\n",
client->adapter->name); client->adapter->dev.name);
return -1; return -1;
} }
printk("tvmixer: debug: %s\n",client->name); printk("tvmixer: debug: %s\n",client->dev.name);
/* unregister ?? */ /* unregister ?? */
for (i = 0; i < DEV_MAX; i++) { for (i = 0; i < DEV_MAX; i++) {
...@@ -273,7 +273,7 @@ static int tvmixer_clients(struct i2c_client *client) ...@@ -273,7 +273,7 @@ static int tvmixer_clients(struct i2c_client *client)
unregister_sound_mixer(devices[i].minor); unregister_sound_mixer(devices[i].minor);
devices[i].dev = NULL; devices[i].dev = NULL;
devices[i].minor = -1; devices[i].minor = -1;
printk("tvmixer: %s unregistered (#1)\n",client->name); printk("tvmixer: %s unregistered (#1)\n",client->dev.name);
return 0; return 0;
} }
} }
...@@ -298,13 +298,13 @@ static int tvmixer_clients(struct i2c_client *client) ...@@ -298,13 +298,13 @@ static int tvmixer_clients(struct i2c_client *client)
if (0 != client->driver->command(client,VIDIOCGAUDIO,&va)) { if (0 != client->driver->command(client,VIDIOCGAUDIO,&va)) {
if (debug) if (debug)
printk("tvmixer: %s: VIDIOCGAUDIO failed\n", printk("tvmixer: %s: VIDIOCGAUDIO failed\n",
client->name); client->dev.name);
return -1; return -1;
} }
if (0 == (va.flags & VIDEO_AUDIO_VOLUME)) { if (0 == (va.flags & VIDEO_AUDIO_VOLUME)) {
if (debug) if (debug)
printk("tvmixer: %s: has no volume control\n", printk("tvmixer: %s: has no volume control\n",
client->name); client->dev.name);
return -1; return -1;
} }
...@@ -318,7 +318,7 @@ static int tvmixer_clients(struct i2c_client *client) ...@@ -318,7 +318,7 @@ static int tvmixer_clients(struct i2c_client *client)
devices[i].count = 0; devices[i].count = 0;
devices[i].dev = client; devices[i].dev = client;
printk("tvmixer: %s (%s) registered with minor %d\n", 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; return 0;
} }
...@@ -344,7 +344,7 @@ static void tvmixer_cleanup_module(void) ...@@ -344,7 +344,7 @@ static void tvmixer_cleanup_module(void)
if (devices[i].minor != -1) { if (devices[i].minor != -1) {
unregister_sound_mixer(devices[i].minor); unregister_sound_mixer(devices[i].minor);
printk("tvmixer: %s unregistered (#2)\n", printk("tvmixer: %s unregistered (#2)\n",
devices[i].dev->name); devices[i].dev->dev.name);
} }
} }
} }
......
/* /*
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 was originally sensors.h - Part of lm_sensors, Linux kernel modules
for hardware monitoring for hardware monitoring
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
...@@ -19,8 +19,8 @@ ...@@ -19,8 +19,8 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#ifndef _LINUX_I2C_PROC_H #ifndef _LINUX_I2C_SENSOR_H
#define _LINUX_I2C_PROC_H #define _LINUX_I2C_SENSOR_H
#include <linux/sysctl.h> #include <linux/sysctl.h>
...@@ -34,46 +34,6 @@ typedef void (*i2c_real_callback) (struct i2c_client * client, ...@@ -34,46 +34,6 @@ typedef void (*i2c_real_callback) (struct i2c_client * client,
#define SENSORS_PROC_REAL_READ 2 #define SENSORS_PROC_REAL_READ 2
#define SENSORS_PROC_REAL_WRITE 3 #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. /* A structure containing detect information.
Force variables overrule all other variables; they force a detection on Force variables overrule all other variables; they force a detection on
that place. If a specific chip is given, the module blindly assumes this that place. If a specific chip is given, the module blindly assumes this
...@@ -368,8 +328,7 @@ struct i2c_address_data { ...@@ -368,8 +328,7 @@ struct i2c_address_data {
SENSORS_INSMOD SENSORS_INSMOD
typedef int i2c_found_addr_proc(struct i2c_adapter *adapter, typedef int i2c_found_addr_proc(struct i2c_adapter *adapter,
int addr, unsigned short flags, int addr, int kind);
int kind);
/* Detect function. It iterates over all possible addresses itself. For /* Detect function. It iterates over all possible addresses itself. For
SMBus addresses, it will only call found_proc if some client is connected SMBus addresses, it will only call found_proc if some client is connected
...@@ -410,5 +369,5 @@ struct i2c_chips_data { ...@@ -410,5 +369,5 @@ struct i2c_chips_data {
char name[SENSORS_PREFIX_MAX + 13]; char name[SENSORS_PREFIX_MAX + 13];
}; };
#endif /* def _LINUX_I2C_PROC_H */ #endif /* def _LINUX_I2C_SENSOR_H */
/*
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);
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment