[PATCH] I2C: Move hwmon drivers (2/3)

Part 2: Move the driver files themselves.

Note that the patch "adds trailing whitespace", because it does move the
files as-is, and some files happen to have trailing whitespace.

From: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Jean Delvare
2005-07-02 18:20:26 +02:00
committed by Greg Kroah-Hartman
vanhempi ad2f931dcb
commit 8d5d45fb14
34 muutettua tiedostoa jossa 0 lisäystä ja 0 poistoa

402
drivers/hwmon/adm1021.c Normal file
Näytä tiedosto

@@ -0,0 +1,402 @@
/*
adm1021.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
Philip Edelbrock <phil@netroedge.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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a,
0x29, 0x2a, 0x2b,
0x4c, 0x4d, 0x4e,
I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1066);
/* adm1021 constants specified below */
/* The adm1021 registers */
/* Read-only */
#define ADM1021_REG_TEMP 0x00
#define ADM1021_REG_REMOTE_TEMP 0x01
#define ADM1021_REG_STATUS 0x02
#define ADM1021_REG_MAN_ID 0x0FE /* 0x41 = AMD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi*/
#define ADM1021_REG_DEV_ID 0x0FF /* ADM1021 = 0x0X, ADM1023 = 0x3X */
#define ADM1021_REG_DIE_CODE 0x0FF /* MAX1617A */
/* These use different addresses for reading/writing */
#define ADM1021_REG_CONFIG_R 0x03
#define ADM1021_REG_CONFIG_W 0x09
#define ADM1021_REG_CONV_RATE_R 0x04
#define ADM1021_REG_CONV_RATE_W 0x0A
/* These are for the ADM1023's additional precision on the remote temp sensor */
#define ADM1021_REG_REM_TEMP_PREC 0x010
#define ADM1021_REG_REM_OFFSET 0x011
#define ADM1021_REG_REM_OFFSET_PREC 0x012
#define ADM1021_REG_REM_TOS_PREC 0x013
#define ADM1021_REG_REM_THYST_PREC 0x014
/* limits */
#define ADM1021_REG_TOS_R 0x05
#define ADM1021_REG_TOS_W 0x0B
#define ADM1021_REG_REMOTE_TOS_R 0x07
#define ADM1021_REG_REMOTE_TOS_W 0x0D
#define ADM1021_REG_THYST_R 0x06
#define ADM1021_REG_THYST_W 0x0C
#define ADM1021_REG_REMOTE_THYST_R 0x08
#define ADM1021_REG_REMOTE_THYST_W 0x0E
/* write-only */
#define ADM1021_REG_ONESHOT 0x0F
/* 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. */
/* Conversions note: 1021 uses normal integer signed-byte format*/
#define TEMP_FROM_REG(val) (val > 127 ? (val-256)*1000 : val*1000)
#define TEMP_TO_REG(val) (SENSORS_LIMIT((val < 0 ? (val/1000)+256 : val/1000),0,255))
/* Initial values */
/* Note: Even though I left the low and high limits named os and hyst,
they don't quite work like a thermostat the way the LM75 does. I.e.,
a lower temp than THYST actually triggers an alarm instead of
clearing it. Weird, ey? --Phil */
/* Each client has this additional data */
struct adm1021_data {
struct i2c_client client;
enum chips type;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u8 temp_max; /* Register values */
u8 temp_hyst;
u8 temp_input;
u8 remote_temp_max;
u8 remote_temp_hyst;
u8 remote_temp_input;
u8 alarms;
/* Special values for ADM1023 only */
u8 remote_temp_prec;
u8 remote_temp_os_prec;
u8 remote_temp_hyst_prec;
u8 remote_temp_offset;
u8 remote_temp_offset_prec;
};
static int adm1021_attach_adapter(struct i2c_adapter *adapter);
static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind);
static void adm1021_init_client(struct i2c_client *client);
static int adm1021_detach_client(struct i2c_client *client);
static int adm1021_read_value(struct i2c_client *client, u8 reg);
static int adm1021_write_value(struct i2c_client *client, u8 reg,
u16 value);
static struct adm1021_data *adm1021_update_device(struct device *dev);
/* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */
static int read_only = 0;
/* This is the driver that will be inserted */
static struct i2c_driver adm1021_driver = {
.owner = THIS_MODULE,
.name = "adm1021",
.id = I2C_DRIVERID_ADM1021,
.flags = I2C_DF_NOTIFY,
.attach_adapter = adm1021_attach_adapter,
.detach_client = adm1021_detach_client,
};
#define show(value) \
static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct adm1021_data *data = adm1021_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \
}
show(temp_max);
show(temp_hyst);
show(temp_input);
show(remote_temp_max);
show(remote_temp_hyst);
show(remote_temp_input);
#define show2(value) \
static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct adm1021_data *data = adm1021_update_device(dev); \
return sprintf(buf, "%d\n", data->value); \
}
show2(alarms);
#define set(value, reg) \
static ssize_t set_##value(struct device *dev, struct device_attribute *attr, 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); \
\
down(&data->update_lock); \
data->value = TEMP_TO_REG(temp); \
adm1021_write_value(client, reg, data->value); \
up(&data->update_lock); \
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(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max);
static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst);
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL);
static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_remote_temp_max, set_remote_temp_max);
static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_remote_temp_hyst, set_remote_temp_hyst);
static DEVICE_ATTR(temp2_input, S_IRUGO, show_remote_temp_input, NULL);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
static int adm1021_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, adm1021_detect);
}
static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i;
struct i2c_client *new_client;
struct adm1021_data *data;
int err = 0;
const char *type_name = "";
/* Make sure we aren't probing the ISA bus!! This is just a safety check
at this moment; i2c_detect really won't call us. */
#ifdef DEBUG
if (i2c_is_isa_adapter(adapter)) {
dev_dbg(&adapter->dev, "adm1021_detect called for an ISA bus adapter?!?\n");
return 0;
}
#endif
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto error0;
/* 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 adm1021_{read,write}_value. */
if (!(data = kmalloc(sizeof(struct adm1021_data), GFP_KERNEL))) {
err = -ENOMEM;
goto error0;
}
memset(data, 0, sizeof(struct adm1021_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &adm1021_driver;
new_client->flags = 0;
/* Now, we do the remaining detection. */
if (kind < 0) {
if ((adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0x03) != 0x00
|| (adm1021_read_value(new_client, ADM1021_REG_CONFIG_R) & 0x3F) != 0x00
|| (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) & 0xF8) != 0x00) {
err = -ENODEV;
goto error1;
}
}
/* Determine the chip type. */
if (kind <= 0) {
i = adm1021_read_value(new_client, ADM1021_REG_MAN_ID);
if (i == 0x41)
if ((adm1021_read_value(new_client, ADM1021_REG_DEV_ID) & 0x0F0) == 0x030)
kind = adm1023;
else
kind = adm1021;
else if (i == 0x49)
kind = thmc10;
else if (i == 0x23)
kind = gl523sm;
else if ((i == 0x4d) &&
(adm1021_read_value(new_client, ADM1021_REG_DEV_ID) == 0x01))
kind = max1617a;
else if (i == 0x54)
kind = mc1066;
/* LM84 Mfr ID in a different place, and it has more unused bits */
else if (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) == 0x00
&& (kind == 0 /* skip extra detection */
|| ((adm1021_read_value(new_client, ADM1021_REG_CONFIG_R) & 0x7F) == 0x00
&& (adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0xAB) == 0x00)))
kind = lm84;
else
kind = max1617;
}
if (kind == max1617) {
type_name = "max1617";
} else if (kind == max1617a) {
type_name = "max1617a";
} else if (kind == adm1021) {
type_name = "adm1021";
} else if (kind == adm1023) {
type_name = "adm1023";
} else if (kind == thmc10) {
type_name = "thmc10";
} else if (kind == lm84) {
type_name = "lm84";
} else if (kind == gl523sm) {
type_name = "gl523sm";
} else if (kind == mc1066) {
type_name = "mc1066";
}
/* Fill in the remaining client fields and put it into the global list */
strlcpy(new_client->name, type_name, I2C_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 error1;
/* Initialize the ADM1021 chip */
if (kind != lm84)
adm1021_init_client(new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_min);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp2_max);
device_create_file(&new_client->dev, &dev_attr_temp2_min);
device_create_file(&new_client->dev, &dev_attr_temp2_input);
device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
error1:
kfree(data);
error0:
return err;
}
static void adm1021_init_client(struct i2c_client *client)
{
/* Enable ADC and disable suspend mode */
adm1021_write_value(client, ADM1021_REG_CONFIG_W,
adm1021_read_value(client, ADM1021_REG_CONFIG_R) & 0xBF);
/* Set Conversion rate to 1/sec (this can be tinkered with) */
adm1021_write_value(client, ADM1021_REG_CONV_RATE_W, 0x04);
}
static int adm1021_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;
}
kfree(i2c_get_clientdata(client));
return 0;
}
/* All registers are byte-sized */
static int adm1021_read_value(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
static int adm1021_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if (!read_only)
return i2c_smbus_write_byte_data(client, reg, value);
return 0;
}
static struct adm1021_data *adm1021_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1021_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
dev_dbg(&client->dev, "Starting adm1021 update\n");
data->temp_input = adm1021_read_value(client, ADM1021_REG_TEMP);
data->temp_max = adm1021_read_value(client, ADM1021_REG_TOS_R);
data->temp_hyst = adm1021_read_value(client, ADM1021_REG_THYST_R);
data->remote_temp_input = adm1021_read_value(client, ADM1021_REG_REMOTE_TEMP);
data->remote_temp_max = adm1021_read_value(client, ADM1021_REG_REMOTE_TOS_R);
data->remote_temp_hyst = adm1021_read_value(client, ADM1021_REG_REMOTE_THYST_R);
data->alarms = adm1021_read_value(client, ADM1021_REG_STATUS) & 0x7c;
if (data->type == adm1023) {
data->remote_temp_prec = adm1021_read_value(client, ADM1021_REG_REM_TEMP_PREC);
data->remote_temp_os_prec = adm1021_read_value(client, ADM1021_REG_REM_TOS_PREC);
data->remote_temp_hyst_prec = adm1021_read_value(client, ADM1021_REG_REM_THYST_PREC);
data->remote_temp_offset = adm1021_read_value(client, ADM1021_REG_REM_OFFSET);
data->remote_temp_offset_prec = adm1021_read_value(client, ADM1021_REG_REM_OFFSET_PREC);
}
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_adm1021_init(void)
{
return i2c_add_driver(&adm1021_driver);
}
static void __exit sensors_adm1021_exit(void)
{
i2c_del_driver(&adm1021_driver);
}
MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl> and "
"Philip Edelbrock <phil@netroedge.com>");
MODULE_DESCRIPTION("adm1021 driver");
MODULE_LICENSE("GPL");
module_param(read_only, bool, 0);
MODULE_PARM_DESC(read_only, "Don't set any values, read only mode");
module_init(sensors_adm1021_init)
module_exit(sensors_adm1021_exit)

577
drivers/hwmon/adm1025.c Normal file
Näytä tiedosto

@@ -0,0 +1,577 @@
/*
* adm1025.c
*
* Copyright (C) 2000 Chen-Yuan Wu <gwu@esoft.com>
* Copyright (C) 2003-2004 Jean Delvare <khali@linux-fr.org>
*
* The ADM1025 is a sensor chip made by Analog Devices. It reports up to 6
* voltages (including its own power source) and up to two temperatures
* (its own plus up to one external one). Voltages are scaled internally
* (which is not the common way) with ratios such that the nominal value
* of each voltage correspond to a register value of 192 (which means a
* resolution of about 0.5% of the nominal value). Temperature values are
* reported with a 1 deg resolution and a 3 deg accuracy. Complete
* datasheet can be obtained from Analog's website at:
* http://www.analog.com/Analog_Root/productPage/productHome/0,2121,ADM1025,00.html
*
* This driver also supports the ADM1025A, which differs from the ADM1025
* only in that it has "open-drain VID inputs while the ADM1025 has
* on-chip 100k pull-ups on the VID inputs". It doesn't make any
* difference for us.
*
* This driver also supports the NE1619, a sensor chip made by Philips.
* That chip is similar to the ADM1025A, with a few differences. The only
* difference that matters to us is that the NE1619 has only two possible
* addresses while the ADM1025A has a third one. Complete datasheet can be
* obtained from Philips's website at:
* http://www.semiconductors.philips.com/pip/NE1619DS.html
*
* Since the ADM1025 was the first chipset supported by this driver, most
* comments will refer to this chipset, but are actually general and
* concern all supported chipsets, unless mentioned otherwise.
*
* 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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/i2c-vid.h>
/*
* Addresses to scan
* ADM1025 and ADM1025A have three possible addresses: 0x2c, 0x2d and 0x2e.
* NE1619 has two possible addresses: 0x2c and 0x2d.
*/
static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_2(adm1025, ne1619);
/*
* The ADM1025 registers
*/
#define ADM1025_REG_MAN_ID 0x3E
#define ADM1025_REG_CHIP_ID 0x3F
#define ADM1025_REG_CONFIG 0x40
#define ADM1025_REG_STATUS1 0x41
#define ADM1025_REG_STATUS2 0x42
#define ADM1025_REG_IN(nr) (0x20 + (nr))
#define ADM1025_REG_IN_MAX(nr) (0x2B + (nr) * 2)
#define ADM1025_REG_IN_MIN(nr) (0x2C + (nr) * 2)
#define ADM1025_REG_TEMP(nr) (0x26 + (nr))
#define ADM1025_REG_TEMP_HIGH(nr) (0x37 + (nr) * 2)
#define ADM1025_REG_TEMP_LOW(nr) (0x38 + (nr) * 2)
#define ADM1025_REG_VID 0x47
#define ADM1025_REG_VID4 0x49
/*
* Conversions and various macros
* The ADM1025 uses signed 8-bit values for temperatures.
*/
static int in_scale[6] = { 2500, 2250, 3300, 5000, 12000, 3300 };
#define IN_FROM_REG(reg,scale) (((reg) * (scale) + 96) / 192)
#define IN_TO_REG(val,scale) ((val) <= 0 ? 0 : \
(val) * 192 >= (scale) * 255 ? 255 : \
((val) * 192 + (scale)/2) / (scale))
#define TEMP_FROM_REG(reg) ((reg) * 1000)
#define TEMP_TO_REG(val) ((val) <= -127500 ? -128 : \
(val) >= 126500 ? 127 : \
(((val) < 0 ? (val)-500 : (val)+500) / 1000))
/*
* Functions declaration
*/
static int adm1025_attach_adapter(struct i2c_adapter *adapter);
static int adm1025_detect(struct i2c_adapter *adapter, int address, int kind);
static void adm1025_init_client(struct i2c_client *client);
static int adm1025_detach_client(struct i2c_client *client);
static struct adm1025_data *adm1025_update_device(struct device *dev);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver adm1025_driver = {
.owner = THIS_MODULE,
.name = "adm1025",
.id = I2C_DRIVERID_ADM1025,
.flags = I2C_DF_NOTIFY,
.attach_adapter = adm1025_attach_adapter,
.detach_client = adm1025_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct adm1025_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
u8 in[6]; /* register value */
u8 in_max[6]; /* register value */
u8 in_min[6]; /* register value */
s8 temp[2]; /* register value */
s8 temp_min[2]; /* register value */
s8 temp_max[2]; /* register value */
u16 alarms; /* register values, combined */
u8 vid; /* register values, combined */
u8 vrm;
};
/*
* Sysfs stuff
*/
#define show_in(offset) \
static ssize_t show_in##offset(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct adm1025_data *data = adm1025_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \
in_scale[offset])); \
} \
static ssize_t show_in##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct adm1025_data *data = adm1025_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \
in_scale[offset])); \
} \
static ssize_t show_in##offset##_max(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct adm1025_data *data = adm1025_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \
in_scale[offset])); \
} \
static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in##offset, NULL);
show_in(0);
show_in(1);
show_in(2);
show_in(3);
show_in(4);
show_in(5);
#define show_temp(offset) \
static ssize_t show_temp##offset(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct adm1025_data *data = adm1025_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[offset-1])); \
} \
static ssize_t show_temp##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct adm1025_data *data = adm1025_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[offset-1])); \
} \
static ssize_t show_temp##offset##_max(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct adm1025_data *data = adm1025_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[offset-1])); \
}\
static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp##offset, NULL);
show_temp(1);
show_temp(2);
#define set_in(offset) \
static ssize_t set_in##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct adm1025_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock); \
data->in_min[offset] = IN_TO_REG(val, in_scale[offset]); \
i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MIN(offset), \
data->in_min[offset]); \
up(&data->update_lock); \
return count; \
} \
static ssize_t set_in##offset##_max(struct device *dev, struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct adm1025_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock); \
data->in_max[offset] = IN_TO_REG(val, in_scale[offset]); \
i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MAX(offset), \
data->in_max[offset]); \
up(&data->update_lock); \
return count; \
} \
static DEVICE_ATTR(in##offset##_min, S_IWUSR | S_IRUGO, \
show_in##offset##_min, set_in##offset##_min); \
static DEVICE_ATTR(in##offset##_max, S_IWUSR | S_IRUGO, \
show_in##offset##_max, set_in##offset##_max);
set_in(0);
set_in(1);
set_in(2);
set_in(3);
set_in(4);
set_in(5);
#define set_temp(offset) \
static ssize_t set_temp##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct adm1025_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock); \
data->temp_min[offset-1] = TEMP_TO_REG(val); \
i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_LOW(offset-1), \
data->temp_min[offset-1]); \
up(&data->update_lock); \
return count; \
} \
static ssize_t set_temp##offset##_max(struct device *dev, struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct adm1025_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock); \
data->temp_max[offset-1] = TEMP_TO_REG(val); \
i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_HIGH(offset-1), \
data->temp_max[offset-1]); \
up(&data->update_lock); \
return count; \
} \
static DEVICE_ATTR(temp##offset##_min, S_IWUSR | S_IRUGO, \
show_temp##offset##_min, set_temp##offset##_min); \
static DEVICE_ATTR(temp##offset##_max, S_IWUSR | S_IRUGO, \
show_temp##offset##_max, set_temp##offset##_max);
set_temp(1);
set_temp(2);
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
{
struct adm1025_data *data = adm1025_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf)
{
struct adm1025_data *data = adm1025_update_device(dev);
return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));
}
/* in1_ref is deprecated in favour of cpu0_vid, remove after 2005-11-11 */
static DEVICE_ATTR(in1_ref, S_IRUGO, show_vid, NULL);
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf)
{
struct adm1025_data *data = adm1025_update_device(dev);
return sprintf(buf, "%u\n", data->vrm);
}
static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1025_data *data = i2c_get_clientdata(client);
data->vrm = simple_strtoul(buf, NULL, 10);
return count;
}
static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
/*
* Real code
*/
static int adm1025_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, adm1025_detect);
}
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
*/
static int adm1025_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct adm1025_data *data;
int err = 0;
const char *name = "";
u8 config;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct adm1025_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct adm1025_data));
/* The common I2C client data is placed right before the
ADM1025-specific data. */
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &adm1025_driver;
new_client->flags = 0;
/*
* Now we do the remaining detection. A negative kind means that
* the driver was loaded with no force parameter (default), so we
* must both detect and identify the chip. A zero kind means that
* the driver was loaded with the force parameter, the detection
* step shall be skipped. A positive kind means that the driver
* was loaded with the force parameter and a given kind of chip is
* requested, so both the detection and the identification steps
* are skipped.
*/
config = i2c_smbus_read_byte_data(new_client, ADM1025_REG_CONFIG);
if (kind < 0) { /* detection */
if ((config & 0x80) != 0x00
|| (i2c_smbus_read_byte_data(new_client,
ADM1025_REG_STATUS1) & 0xC0) != 0x00
|| (i2c_smbus_read_byte_data(new_client,
ADM1025_REG_STATUS2) & 0xBC) != 0x00) {
dev_dbg(&adapter->dev,
"ADM1025 detection failed at 0x%02x.\n",
address);
goto exit_free;
}
}
if (kind <= 0) { /* identification */
u8 man_id, chip_id;
man_id = i2c_smbus_read_byte_data(new_client,
ADM1025_REG_MAN_ID);
chip_id = i2c_smbus_read_byte_data(new_client,
ADM1025_REG_CHIP_ID);
if (man_id == 0x41) { /* Analog Devices */
if ((chip_id & 0xF0) == 0x20) { /* ADM1025/ADM1025A */
kind = adm1025;
}
} else
if (man_id == 0xA1) { /* Philips */
if (address != 0x2E
&& (chip_id & 0xF0) == 0x20) { /* NE1619 */
kind = ne1619;
}
}
if (kind <= 0) { /* identification failed */
dev_info(&adapter->dev,
"Unsupported chip (man_id=0x%02X, "
"chip_id=0x%02X).\n", man_id, chip_id);
goto exit_free;
}
}
if (kind == adm1025) {
name = "adm1025";
} else if (kind == ne1619) {
name = "ne1619";
}
/* We can fill in the remaining client fields */
strlcpy(new_client->name, name, I2C_NAME_SIZE);
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 exit_free;
/* Initialize the ADM1025 chip */
adm1025_init_client(new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_in0_input);
device_create_file(&new_client->dev, &dev_attr_in1_input);
device_create_file(&new_client->dev, &dev_attr_in2_input);
device_create_file(&new_client->dev, &dev_attr_in3_input);
device_create_file(&new_client->dev, &dev_attr_in5_input);
device_create_file(&new_client->dev, &dev_attr_in0_min);
device_create_file(&new_client->dev, &dev_attr_in1_min);
device_create_file(&new_client->dev, &dev_attr_in2_min);
device_create_file(&new_client->dev, &dev_attr_in3_min);
device_create_file(&new_client->dev, &dev_attr_in5_min);
device_create_file(&new_client->dev, &dev_attr_in0_max);
device_create_file(&new_client->dev, &dev_attr_in1_max);
device_create_file(&new_client->dev, &dev_attr_in2_max);
device_create_file(&new_client->dev, &dev_attr_in3_max);
device_create_file(&new_client->dev, &dev_attr_in5_max);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp2_input);
device_create_file(&new_client->dev, &dev_attr_temp1_min);
device_create_file(&new_client->dev, &dev_attr_temp2_min);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp2_max);
device_create_file(&new_client->dev, &dev_attr_alarms);
/* in1_ref is deprecated, remove after 2005-11-11 */
device_create_file(&new_client->dev, &dev_attr_in1_ref);
device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
device_create_file(&new_client->dev, &dev_attr_vrm);
/* Pin 11 is either in4 (+12V) or VID4 */
if (!(config & 0x20)) {
device_create_file(&new_client->dev, &dev_attr_in4_input);
device_create_file(&new_client->dev, &dev_attr_in4_min);
device_create_file(&new_client->dev, &dev_attr_in4_max);
}
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static void adm1025_init_client(struct i2c_client *client)
{
u8 reg;
struct adm1025_data *data = i2c_get_clientdata(client);
int i;
data->vrm = i2c_which_vrm();
/*
* Set high limits
* Usually we avoid setting limits on driver init, but it happens
* that the ADM1025 comes with stupid default limits (all registers
* set to 0). In case the chip has not gone through any limit
* setting yet, we better set the high limits to the max so that
* no alarm triggers.
*/
for (i=0; i<6; i++) {
reg = i2c_smbus_read_byte_data(client,
ADM1025_REG_IN_MAX(i));
if (reg == 0)
i2c_smbus_write_byte_data(client,
ADM1025_REG_IN_MAX(i),
0xFF);
}
for (i=0; i<2; i++) {
reg = i2c_smbus_read_byte_data(client,
ADM1025_REG_TEMP_HIGH(i));
if (reg == 0)
i2c_smbus_write_byte_data(client,
ADM1025_REG_TEMP_HIGH(i),
0x7F);
}
/*
* Start the conversions
*/
reg = i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG);
if (!(reg & 0x01))
i2c_smbus_write_byte_data(client, ADM1025_REG_CONFIG,
(reg&0x7E)|0x01);
}
static int adm1025_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;
}
kfree(i2c_get_clientdata(client));
return 0;
}
static struct adm1025_data *adm1025_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1025_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
int i;
dev_dbg(&client->dev, "Updating data.\n");
for (i=0; i<6; i++) {
data->in[i] = i2c_smbus_read_byte_data(client,
ADM1025_REG_IN(i));
data->in_min[i] = i2c_smbus_read_byte_data(client,
ADM1025_REG_IN_MIN(i));
data->in_max[i] = i2c_smbus_read_byte_data(client,
ADM1025_REG_IN_MAX(i));
}
for (i=0; i<2; i++) {
data->temp[i] = i2c_smbus_read_byte_data(client,
ADM1025_REG_TEMP(i));
data->temp_min[i] = i2c_smbus_read_byte_data(client,
ADM1025_REG_TEMP_LOW(i));
data->temp_max[i] = i2c_smbus_read_byte_data(client,
ADM1025_REG_TEMP_HIGH(i));
}
data->alarms = i2c_smbus_read_byte_data(client,
ADM1025_REG_STATUS1)
| (i2c_smbus_read_byte_data(client,
ADM1025_REG_STATUS2) << 8);
data->vid = (i2c_smbus_read_byte_data(client,
ADM1025_REG_VID) & 0x0f)
| ((i2c_smbus_read_byte_data(client,
ADM1025_REG_VID4) & 0x01) << 4);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_adm1025_init(void)
{
return i2c_add_driver(&adm1025_driver);
}
static void __exit sensors_adm1025_exit(void)
{
i2c_del_driver(&adm1025_driver);
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("ADM1025 driver");
MODULE_LICENSE("GPL");
module_init(sensors_adm1025_init);
module_exit(sensors_adm1025_exit);

1714
drivers/hwmon/adm1026.c Normal file

File diff suppressed because it is too large Load Diff

977
drivers/hwmon/adm1031.c Normal file
Näytä tiedosto

@@ -0,0 +1,977 @@
/*
adm1031.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Based on lm75.c and lm85.c
Supports adm1030 / adm1031
Copyright (C) 2004 Alexandre d'Alton <alex@alexdalton.org>
Reworked by Jean Delvare <khali@linux-fr.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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* Following macros takes channel parameter starting from 0 to 2 */
#define ADM1031_REG_FAN_SPEED(nr) (0x08 + (nr))
#define ADM1031_REG_FAN_DIV(nr) (0x20 + (nr))
#define ADM1031_REG_PWM (0x22)
#define ADM1031_REG_FAN_MIN(nr) (0x10 + (nr))
#define ADM1031_REG_TEMP_MAX(nr) (0x14 + 4*(nr))
#define ADM1031_REG_TEMP_MIN(nr) (0x15 + 4*(nr))
#define ADM1031_REG_TEMP_CRIT(nr) (0x16 + 4*(nr))
#define ADM1031_REG_TEMP(nr) (0xa + (nr))
#define ADM1031_REG_AUTO_TEMP(nr) (0x24 + (nr))
#define ADM1031_REG_STATUS(nr) (0x2 + (nr))
#define ADM1031_REG_CONF1 0x0
#define ADM1031_REG_CONF2 0x1
#define ADM1031_REG_EXT_TEMP 0x6
#define ADM1031_CONF1_MONITOR_ENABLE 0x01 /* Monitoring enable */
#define ADM1031_CONF1_PWM_INVERT 0x08 /* PWM Invert */
#define ADM1031_CONF1_AUTO_MODE 0x80 /* Auto FAN */
#define ADM1031_CONF2_PWM1_ENABLE 0x01
#define ADM1031_CONF2_PWM2_ENABLE 0x02
#define ADM1031_CONF2_TACH1_ENABLE 0x04
#define ADM1031_CONF2_TACH2_ENABLE 0x08
#define ADM1031_CONF2_TEMP_ENABLE(chan) (0x10 << (chan))
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_2(adm1030, adm1031);
typedef u8 auto_chan_table_t[8][2];
/* Each client has this additional data */
struct adm1031_data {
struct i2c_client client;
struct semaphore update_lock;
int chip_type;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
/* The chan_select_table contains the possible configurations for
* auto fan control.
*/
auto_chan_table_t *chan_select_table;
u16 alarm;
u8 conf1;
u8 conf2;
u8 fan[2];
u8 fan_div[2];
u8 fan_min[2];
u8 pwm[2];
u8 old_pwm[2];
s8 temp[3];
u8 ext_temp[3];
u8 auto_temp[3];
u8 auto_temp_min[3];
u8 auto_temp_off[3];
u8 auto_temp_max[3];
s8 temp_min[3];
s8 temp_max[3];
s8 temp_crit[3];
};
static int adm1031_attach_adapter(struct i2c_adapter *adapter);
static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind);
static void adm1031_init_client(struct i2c_client *client);
static int adm1031_detach_client(struct i2c_client *client);
static struct adm1031_data *adm1031_update_device(struct device *dev);
/* This is the driver that will be inserted */
static struct i2c_driver adm1031_driver = {
.owner = THIS_MODULE,
.name = "adm1031",
.flags = I2C_DF_NOTIFY,
.attach_adapter = adm1031_attach_adapter,
.detach_client = adm1031_detach_client,
};
static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
static inline int
adm1031_write_value(struct i2c_client *client, u8 reg, unsigned int value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
#define TEMP_TO_REG(val) (((val) < 0 ? ((val - 500) / 1000) : \
((val + 500) / 1000)))
#define TEMP_FROM_REG(val) ((val) * 1000)
#define TEMP_FROM_REG_EXT(val, ext) (TEMP_FROM_REG(val) + (ext) * 125)
#define FAN_FROM_REG(reg, div) ((reg) ? (11250 * 60) / ((reg) * (div)) : 0)
static int FAN_TO_REG(int reg, int div)
{
int tmp;
tmp = FAN_FROM_REG(SENSORS_LIMIT(reg, 0, 65535), div);
return tmp > 255 ? 255 : tmp;
}
#define FAN_DIV_FROM_REG(reg) (1<<(((reg)&0xc0)>>6))
#define PWM_TO_REG(val) (SENSORS_LIMIT((val), 0, 255) >> 4)
#define PWM_FROM_REG(val) ((val) << 4)
#define FAN_CHAN_FROM_REG(reg) (((reg) >> 5) & 7)
#define FAN_CHAN_TO_REG(val, reg) \
(((reg) & 0x1F) | (((val) << 5) & 0xe0))
#define AUTO_TEMP_MIN_TO_REG(val, reg) \
((((val)/500) & 0xf8)|((reg) & 0x7))
#define AUTO_TEMP_RANGE_FROM_REG(reg) (5000 * (1<< ((reg)&0x7)))
#define AUTO_TEMP_MIN_FROM_REG(reg) (1000 * ((((reg) >> 3) & 0x1f) << 2))
#define AUTO_TEMP_MIN_FROM_REG_DEG(reg) ((((reg) >> 3) & 0x1f) << 2)
#define AUTO_TEMP_OFF_FROM_REG(reg) \
(AUTO_TEMP_MIN_FROM_REG(reg) - 5000)
#define AUTO_TEMP_MAX_FROM_REG(reg) \
(AUTO_TEMP_RANGE_FROM_REG(reg) + \
AUTO_TEMP_MIN_FROM_REG(reg))
static int AUTO_TEMP_MAX_TO_REG(int val, int reg, int pwm)
{
int ret;
int range = val - AUTO_TEMP_MIN_FROM_REG(reg);
range = ((val - AUTO_TEMP_MIN_FROM_REG(reg))*10)/(16 - pwm);
ret = ((reg & 0xf8) |
(range < 10000 ? 0 :
range < 20000 ? 1 :
range < 40000 ? 2 : range < 80000 ? 3 : 4));
return ret;
}
/* FAN auto control */
#define GET_FAN_AUTO_BITFIELD(data, idx) \
(*(data)->chan_select_table)[FAN_CHAN_FROM_REG((data)->conf1)][idx%2]
/* The tables below contains the possible values for the auto fan
* control bitfields. the index in the table is the register value.
* MSb is the auto fan control enable bit, so the four first entries
* in the table disables auto fan control when both bitfields are zero.
*/
static auto_chan_table_t auto_channel_select_table_adm1031 = {
{0, 0}, {0, 0}, {0, 0}, {0, 0},
{2 /*0b010 */ , 4 /*0b100 */ },
{2 /*0b010 */ , 2 /*0b010 */ },
{4 /*0b100 */ , 4 /*0b100 */ },
{7 /*0b111 */ , 7 /*0b111 */ },
};
static auto_chan_table_t auto_channel_select_table_adm1030 = {
{0, 0}, {0, 0}, {0, 0}, {0, 0},
{2 /*0b10 */ , 0},
{0xff /*invalid */ , 0},
{0xff /*invalid */ , 0},
{3 /*0b11 */ , 0},
};
/* That function checks if a bitfield is valid and returns the other bitfield
* nearest match if no exact match where found.
*/
static int
get_fan_auto_nearest(struct adm1031_data *data,
int chan, u8 val, u8 reg, u8 * new_reg)
{
int i;
int first_match = -1, exact_match = -1;
u8 other_reg_val =
(*data->chan_select_table)[FAN_CHAN_FROM_REG(reg)][chan ? 0 : 1];
if (val == 0) {
*new_reg = 0;
return 0;
}
for (i = 0; i < 8; i++) {
if ((val == (*data->chan_select_table)[i][chan]) &&
((*data->chan_select_table)[i][chan ? 0 : 1] ==
other_reg_val)) {
/* We found an exact match */
exact_match = i;
break;
} else if (val == (*data->chan_select_table)[i][chan] &&
first_match == -1) {
/* Save the first match in case of an exact match has not been
* found
*/
first_match = i;
}
}
if (exact_match >= 0) {
*new_reg = exact_match;
} else if (first_match >= 0) {
*new_reg = first_match;
} else {
return -EINVAL;
}
return 0;
}
static ssize_t show_fan_auto_channel(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n", GET_FAN_AUTO_BITFIELD(data, nr));
}
static ssize_t
set_fan_auto_channel(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
u8 reg;
int ret;
u8 old_fan_mode;
old_fan_mode = data->conf1;
down(&data->update_lock);
if ((ret = get_fan_auto_nearest(data, nr, val, data->conf1, &reg))) {
up(&data->update_lock);
return ret;
}
if (((data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1)) & ADM1031_CONF1_AUTO_MODE) ^
(old_fan_mode & ADM1031_CONF1_AUTO_MODE)) {
if (data->conf1 & ADM1031_CONF1_AUTO_MODE){
/* Switch to Auto Fan Mode
* Save PWM registers
* Set PWM registers to 33% Both */
data->old_pwm[0] = data->pwm[0];
data->old_pwm[1] = data->pwm[1];
adm1031_write_value(client, ADM1031_REG_PWM, 0x55);
} else {
/* Switch to Manual Mode */
data->pwm[0] = data->old_pwm[0];
data->pwm[1] = data->old_pwm[1];
/* Restore PWM registers */
adm1031_write_value(client, ADM1031_REG_PWM,
data->pwm[0] | (data->pwm[1] << 4));
}
}
data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1);
adm1031_write_value(client, ADM1031_REG_CONF1, data->conf1);
up(&data->update_lock);
return count;
}
#define fan_auto_channel_offset(offset) \
static ssize_t show_fan_auto_channel_##offset (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_fan_auto_channel(dev, buf, offset - 1); \
} \
static ssize_t set_fan_auto_channel_##offset (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_fan_auto_channel(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(auto_fan##offset##_channel, S_IRUGO | S_IWUSR, \
show_fan_auto_channel_##offset, \
set_fan_auto_channel_##offset)
fan_auto_channel_offset(1);
fan_auto_channel_offset(2);
/* Auto Temps */
static ssize_t show_auto_temp_off(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n",
AUTO_TEMP_OFF_FROM_REG(data->auto_temp[nr]));
}
static ssize_t show_auto_temp_min(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n",
AUTO_TEMP_MIN_FROM_REG(data->auto_temp[nr]));
}
static ssize_t
set_auto_temp_min(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->auto_temp[nr] = AUTO_TEMP_MIN_TO_REG(val, data->auto_temp[nr]);
adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr),
data->auto_temp[nr]);
up(&data->update_lock);
return count;
}
static ssize_t show_auto_temp_max(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n",
AUTO_TEMP_MAX_FROM_REG(data->auto_temp[nr]));
}
static ssize_t
set_auto_temp_max(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_max[nr] = AUTO_TEMP_MAX_TO_REG(val, data->auto_temp[nr], data->pwm[nr]);
adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr),
data->temp_max[nr]);
up(&data->update_lock);
return count;
}
#define auto_temp_reg(offset) \
static ssize_t show_auto_temp_##offset##_off (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_auto_temp_off(dev, buf, offset - 1); \
} \
static ssize_t show_auto_temp_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_auto_temp_min(dev, buf, offset - 1); \
} \
static ssize_t show_auto_temp_##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_auto_temp_max(dev, buf, offset - 1); \
} \
static ssize_t set_auto_temp_##offset##_min (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_auto_temp_min(dev, buf, count, offset - 1); \
} \
static ssize_t set_auto_temp_##offset##_max (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_auto_temp_max(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(auto_temp##offset##_off, S_IRUGO, \
show_auto_temp_##offset##_off, NULL); \
static DEVICE_ATTR(auto_temp##offset##_min, S_IRUGO | S_IWUSR, \
show_auto_temp_##offset##_min, set_auto_temp_##offset##_min);\
static DEVICE_ATTR(auto_temp##offset##_max, S_IRUGO | S_IWUSR, \
show_auto_temp_##offset##_max, set_auto_temp_##offset##_max)
auto_temp_reg(1);
auto_temp_reg(2);
auto_temp_reg(3);
/* pwm */
static ssize_t show_pwm(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr]));
}
static ssize_t
set_pwm(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
int reg;
down(&data->update_lock);
if ((data->conf1 & ADM1031_CONF1_AUTO_MODE) &&
(((val>>4) & 0xf) != 5)) {
/* In automatic mode, the only PWM accepted is 33% */
up(&data->update_lock);
return -EINVAL;
}
data->pwm[nr] = PWM_TO_REG(val);
reg = adm1031_read_value(client, ADM1031_REG_PWM);
adm1031_write_value(client, ADM1031_REG_PWM,
nr ? ((data->pwm[nr] << 4) & 0xf0) | (reg & 0xf)
: (data->pwm[nr] & 0xf) | (reg & 0xf0));
up(&data->update_lock);
return count;
}
#define pwm_reg(offset) \
static ssize_t show_pwm_##offset (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_pwm(dev, buf, offset - 1); \
} \
static ssize_t set_pwm_##offset (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_pwm(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
show_pwm_##offset, set_pwm_##offset)
pwm_reg(1);
pwm_reg(2);
/* Fans */
/*
* That function checks the cases where the fan reading is not
* relevant. It is used to provide 0 as fan reading when the fan is
* not supposed to run
*/
static int trust_fan_readings(struct adm1031_data *data, int chan)
{
int res = 0;
if (data->conf1 & ADM1031_CONF1_AUTO_MODE) {
switch (data->conf1 & 0x60) {
case 0x00: /* remote temp1 controls fan1 remote temp2 controls fan2 */
res = data->temp[chan+1] >=
AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[chan+1]);
break;
case 0x20: /* remote temp1 controls both fans */
res =
data->temp[1] >=
AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[1]);
break;
case 0x40: /* remote temp2 controls both fans */
res =
data->temp[2] >=
AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[2]);
break;
case 0x60: /* max controls both fans */
res =
data->temp[0] >=
AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[0])
|| data->temp[1] >=
AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[1])
|| (data->chip_type == adm1031
&& data->temp[2] >=
AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[2]));
break;
}
} else {
res = data->pwm[chan] > 0;
}
return res;
}
static ssize_t show_fan(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
int value;
value = trust_fan_readings(data, nr) ? FAN_FROM_REG(data->fan[nr],
FAN_DIV_FROM_REG(data->fan_div[nr])) : 0;
return sprintf(buf, "%d\n", value);
}
static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n", FAN_DIV_FROM_REG(data->fan_div[nr]));
}
static ssize_t show_fan_min(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n",
FAN_FROM_REG(data->fan_min[nr],
FAN_DIV_FROM_REG(data->fan_div[nr])));
}
static ssize_t
set_fan_min(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
if (val) {
data->fan_min[nr] =
FAN_TO_REG(val, FAN_DIV_FROM_REG(data->fan_div[nr]));
} else {
data->fan_min[nr] = 0xff;
}
adm1031_write_value(client, ADM1031_REG_FAN_MIN(nr), data->fan_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t
set_fan_div(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
u8 tmp;
int old_div;
int new_min;
tmp = val == 8 ? 0xc0 :
val == 4 ? 0x80 :
val == 2 ? 0x40 :
val == 1 ? 0x00 :
0xff;
if (tmp == 0xff)
return -EINVAL;
down(&data->update_lock);
old_div = FAN_DIV_FROM_REG(data->fan_div[nr]);
data->fan_div[nr] = (tmp & 0xC0) | (0x3f & data->fan_div[nr]);
new_min = data->fan_min[nr] * old_div /
FAN_DIV_FROM_REG(data->fan_div[nr]);
data->fan_min[nr] = new_min > 0xff ? 0xff : new_min;
data->fan[nr] = data->fan[nr] * old_div /
FAN_DIV_FROM_REG(data->fan_div[nr]);
adm1031_write_value(client, ADM1031_REG_FAN_DIV(nr),
data->fan_div[nr]);
adm1031_write_value(client, ADM1031_REG_FAN_MIN(nr),
data->fan_min[nr]);
up(&data->update_lock);
return count;
}
#define fan_offset(offset) \
static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_fan(dev, buf, offset - 1); \
} \
static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_fan_min(dev, buf, offset - 1); \
} \
static ssize_t show_fan_##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_fan_div(dev, buf, offset - 1); \
} \
static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_fan_min(dev, buf, count, offset - 1); \
} \
static ssize_t set_fan_##offset##_div (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_fan_div(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, \
NULL); \
static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
show_fan_##offset##_min, set_fan_##offset##_min); \
static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
show_fan_##offset##_div, set_fan_##offset##_div); \
static DEVICE_ATTR(auto_fan##offset##_min_pwm, S_IRUGO | S_IWUSR, \
show_pwm_##offset, set_pwm_##offset)
fan_offset(1);
fan_offset(2);
/* Temps */
static ssize_t show_temp(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
int ext;
ext = nr == 0 ?
((data->ext_temp[nr] >> 6) & 0x3) * 2 :
(((data->ext_temp[nr] >> ((nr - 1) * 3)) & 7));
return sprintf(buf, "%d\n", TEMP_FROM_REG_EXT(data->temp[nr], ext));
}
static ssize_t show_temp_min(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[nr]));
}
static ssize_t show_temp_max(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[nr]));
}
static ssize_t show_temp_crit(struct device *dev, char *buf, int nr)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit[nr]));
}
static ssize_t
set_temp_min(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int val;
val = simple_strtol(buf, NULL, 10);
val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875);
down(&data->update_lock);
data->temp_min[nr] = TEMP_TO_REG(val);
adm1031_write_value(client, ADM1031_REG_TEMP_MIN(nr),
data->temp_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t
set_temp_max(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int val;
val = simple_strtol(buf, NULL, 10);
val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875);
down(&data->update_lock);
data->temp_max[nr] = TEMP_TO_REG(val);
adm1031_write_value(client, ADM1031_REG_TEMP_MAX(nr),
data->temp_max[nr]);
up(&data->update_lock);
return count;
}
static ssize_t
set_temp_crit(struct device *dev, const char *buf, size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int val;
val = simple_strtol(buf, NULL, 10);
val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875);
down(&data->update_lock);
data->temp_crit[nr] = TEMP_TO_REG(val);
adm1031_write_value(client, ADM1031_REG_TEMP_CRIT(nr),
data->temp_crit[nr]);
up(&data->update_lock);
return count;
}
#define temp_reg(offset) \
static ssize_t show_temp_##offset (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_temp(dev, buf, offset - 1); \
} \
static ssize_t show_temp_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_temp_min(dev, buf, offset - 1); \
} \
static ssize_t show_temp_##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_temp_max(dev, buf, offset - 1); \
} \
static ssize_t show_temp_##offset##_crit (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_temp_crit(dev, buf, offset - 1); \
} \
static ssize_t set_temp_##offset##_min (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_temp_min(dev, buf, count, offset - 1); \
} \
static ssize_t set_temp_##offset##_max (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_temp_max(dev, buf, count, offset - 1); \
} \
static ssize_t set_temp_##offset##_crit (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_temp_crit(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, \
NULL); \
static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \
show_temp_##offset##_min, set_temp_##offset##_min); \
static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \
show_temp_##offset##_max, set_temp_##offset##_max); \
static DEVICE_ATTR(temp##offset##_crit, S_IRUGO | S_IWUSR, \
show_temp_##offset##_crit, set_temp_##offset##_crit)
temp_reg(1);
temp_reg(2);
temp_reg(3);
/* Alarms */
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
{
struct adm1031_data *data = adm1031_update_device(dev);
return sprintf(buf, "%d\n", data->alarm);
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
static int adm1031_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, adm1031_detect);
}
/* This function is called by i2c_detect */
static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct adm1031_data *data;
int err = 0;
const char *name = "";
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct adm1031_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct adm1031_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &adm1031_driver;
new_client->flags = 0;
if (kind < 0) {
int id, co;
id = i2c_smbus_read_byte_data(new_client, 0x3d);
co = i2c_smbus_read_byte_data(new_client, 0x3e);
if (!((id == 0x31 || id == 0x30) && co == 0x41))
goto exit_free;
kind = (id == 0x30) ? adm1030 : adm1031;
}
if (kind <= 0)
kind = adm1031;
/* Given the detected chip type, set the chip name and the
* auto fan control helper table. */
if (kind == adm1030) {
name = "adm1030";
data->chan_select_table = &auto_channel_select_table_adm1030;
} else if (kind == adm1031) {
name = "adm1031";
data->chan_select_table = &auto_channel_select_table_adm1031;
}
data->chip_type = kind;
strlcpy(new_client->name, name, I2C_NAME_SIZE);
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 exit_free;
/* Initialize the ADM1031 chip */
adm1031_init_client(new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_fan1_input);
device_create_file(&new_client->dev, &dev_attr_fan1_div);
device_create_file(&new_client->dev, &dev_attr_fan1_min);
device_create_file(&new_client->dev, &dev_attr_pwm1);
device_create_file(&new_client->dev, &dev_attr_auto_fan1_channel);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_min);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_crit);
device_create_file(&new_client->dev, &dev_attr_temp2_input);
device_create_file(&new_client->dev, &dev_attr_temp2_min);
device_create_file(&new_client->dev, &dev_attr_temp2_max);
device_create_file(&new_client->dev, &dev_attr_temp2_crit);
device_create_file(&new_client->dev, &dev_attr_auto_temp1_off);
device_create_file(&new_client->dev, &dev_attr_auto_temp1_min);
device_create_file(&new_client->dev, &dev_attr_auto_temp1_max);
device_create_file(&new_client->dev, &dev_attr_auto_temp2_off);
device_create_file(&new_client->dev, &dev_attr_auto_temp2_min);
device_create_file(&new_client->dev, &dev_attr_auto_temp2_max);
device_create_file(&new_client->dev, &dev_attr_auto_fan1_min_pwm);
device_create_file(&new_client->dev, &dev_attr_alarms);
if (kind == adm1031) {
device_create_file(&new_client->dev, &dev_attr_fan2_input);
device_create_file(&new_client->dev, &dev_attr_fan2_div);
device_create_file(&new_client->dev, &dev_attr_fan2_min);
device_create_file(&new_client->dev, &dev_attr_pwm2);
device_create_file(&new_client->dev,
&dev_attr_auto_fan2_channel);
device_create_file(&new_client->dev, &dev_attr_temp3_input);
device_create_file(&new_client->dev, &dev_attr_temp3_min);
device_create_file(&new_client->dev, &dev_attr_temp3_max);
device_create_file(&new_client->dev, &dev_attr_temp3_crit);
device_create_file(&new_client->dev, &dev_attr_auto_temp3_off);
device_create_file(&new_client->dev, &dev_attr_auto_temp3_min);
device_create_file(&new_client->dev, &dev_attr_auto_temp3_max);
device_create_file(&new_client->dev, &dev_attr_auto_fan2_min_pwm);
}
return 0;
exit_free:
kfree(new_client);
exit:
return err;
}
static int adm1031_detach_client(struct i2c_client *client)
{
int ret;
if ((ret = i2c_detach_client(client)) != 0) {
return ret;
}
kfree(client);
return 0;
}
static void adm1031_init_client(struct i2c_client *client)
{
unsigned int read_val;
unsigned int mask;
struct adm1031_data *data = i2c_get_clientdata(client);
mask = (ADM1031_CONF2_PWM1_ENABLE | ADM1031_CONF2_TACH1_ENABLE);
if (data->chip_type == adm1031) {
mask |= (ADM1031_CONF2_PWM2_ENABLE |
ADM1031_CONF2_TACH2_ENABLE);
}
/* Initialize the ADM1031 chip (enables fan speed reading ) */
read_val = adm1031_read_value(client, ADM1031_REG_CONF2);
if ((read_val | mask) != read_val) {
adm1031_write_value(client, ADM1031_REG_CONF2, read_val | mask);
}
read_val = adm1031_read_value(client, ADM1031_REG_CONF1);
if ((read_val | ADM1031_CONF1_MONITOR_ENABLE) != read_val) {
adm1031_write_value(client, ADM1031_REG_CONF1, read_val |
ADM1031_CONF1_MONITOR_ENABLE);
}
}
static struct adm1031_data *adm1031_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm1031_data *data = i2c_get_clientdata(client);
int chan;
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
dev_dbg(&client->dev, "Starting adm1031 update\n");
for (chan = 0;
chan < ((data->chip_type == adm1031) ? 3 : 2); chan++) {
u8 oldh, newh;
oldh =
adm1031_read_value(client, ADM1031_REG_TEMP(chan));
data->ext_temp[chan] =
adm1031_read_value(client, ADM1031_REG_EXT_TEMP);
newh =
adm1031_read_value(client, ADM1031_REG_TEMP(chan));
if (newh != oldh) {
data->ext_temp[chan] =
adm1031_read_value(client,
ADM1031_REG_EXT_TEMP);
#ifdef DEBUG
oldh =
adm1031_read_value(client,
ADM1031_REG_TEMP(chan));
/* oldh is actually newer */
if (newh != oldh)
dev_warn(&client->dev,
"Remote temperature may be "
"wrong.\n");
#endif
}
data->temp[chan] = newh;
data->temp_min[chan] =
adm1031_read_value(client,
ADM1031_REG_TEMP_MIN(chan));
data->temp_max[chan] =
adm1031_read_value(client,
ADM1031_REG_TEMP_MAX(chan));
data->temp_crit[chan] =
adm1031_read_value(client,
ADM1031_REG_TEMP_CRIT(chan));
data->auto_temp[chan] =
adm1031_read_value(client,
ADM1031_REG_AUTO_TEMP(chan));
}
data->conf1 = adm1031_read_value(client, ADM1031_REG_CONF1);
data->conf2 = adm1031_read_value(client, ADM1031_REG_CONF2);
data->alarm = adm1031_read_value(client, ADM1031_REG_STATUS(0))
| (adm1031_read_value(client, ADM1031_REG_STATUS(1))
<< 8);
if (data->chip_type == adm1030) {
data->alarm &= 0xc0ff;
}
for (chan=0; chan<(data->chip_type == adm1030 ? 1 : 2); chan++) {
data->fan_div[chan] =
adm1031_read_value(client, ADM1031_REG_FAN_DIV(chan));
data->fan_min[chan] =
adm1031_read_value(client, ADM1031_REG_FAN_MIN(chan));
data->fan[chan] =
adm1031_read_value(client, ADM1031_REG_FAN_SPEED(chan));
data->pwm[chan] =
0xf & (adm1031_read_value(client, ADM1031_REG_PWM) >>
(4*chan));
}
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_adm1031_init(void)
{
return i2c_add_driver(&adm1031_driver);
}
static void __exit sensors_adm1031_exit(void)
{
i2c_del_driver(&adm1031_driver);
}
MODULE_AUTHOR("Alexandre d'Alton <alex@alexdalton.org>");
MODULE_DESCRIPTION("ADM1031/ADM1030 driver");
MODULE_LICENSE("GPL");
module_init(sensors_adm1031_init);
module_exit(sensors_adm1031_exit);

791
drivers/hwmon/adm9240.c Normal file
Näytä tiedosto

@@ -0,0 +1,791 @@
/*
* adm9240.c Part of lm_sensors, Linux kernel modules for hardware
* monitoring
*
* Copyright (C) 1999 Frodo Looijaard <frodol@dds.nl>
* Philip Edelbrock <phil@netroedge.com>
* Copyright (C) 2003 Michiel Rook <michiel@grendelproject.nl>
* Copyright (C) 2005 Grant Coady <gcoady@gmail.com> with valuable
* guidance from Jean Delvare
*
* Driver supports Analog Devices ADM9240
* Dallas Semiconductor DS1780
* National Semiconductor LM81
*
* ADM9240 is the reference, DS1780 and LM81 are register compatibles
*
* Voltage Six inputs are scaled by chip, VID also reported
* Temperature Chip temperature to 0.5'C, maximum and max_hysteris
* Fans 2 fans, low speed alarm, automatic fan clock divider
* Alarms 16-bit map of active alarms
* Analog Out 0..1250 mV output
*
* Chassis Intrusion: clear CI latch with 'echo 1 > chassis_clear'
*
* Test hardware: Intel SE440BX-2 desktop motherboard --Grant
*
* LM81 extended temp reading not implemented
*
* 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.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/i2c-vid.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f,
I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_3(adm9240, ds1780, lm81);
/* ADM9240 registers */
#define ADM9240_REG_MAN_ID 0x3e
#define ADM9240_REG_DIE_REV 0x3f
#define ADM9240_REG_CONFIG 0x40
#define ADM9240_REG_IN(nr) (0x20 + (nr)) /* 0..5 */
#define ADM9240_REG_IN_MAX(nr) (0x2b + (nr) * 2)
#define ADM9240_REG_IN_MIN(nr) (0x2c + (nr) * 2)
#define ADM9240_REG_FAN(nr) (0x28 + (nr)) /* 0..1 */
#define ADM9240_REG_FAN_MIN(nr) (0x3b + (nr))
#define ADM9240_REG_INT(nr) (0x41 + (nr))
#define ADM9240_REG_INT_MASK(nr) (0x43 + (nr))
#define ADM9240_REG_TEMP 0x27
#define ADM9240_REG_TEMP_HIGH 0x39
#define ADM9240_REG_TEMP_HYST 0x3a
#define ADM9240_REG_ANALOG_OUT 0x19
#define ADM9240_REG_CHASSIS_CLEAR 0x46
#define ADM9240_REG_VID_FAN_DIV 0x47
#define ADM9240_REG_I2C_ADDR 0x48
#define ADM9240_REG_VID4 0x49
#define ADM9240_REG_TEMP_CONF 0x4b
/* generalised scaling with integer rounding */
static inline int SCALE(long val, int mul, int div)
{
if (val < 0)
return (val * mul - div / 2) / div;
else
return (val * mul + div / 2) / div;
}
/* adm9240 internally scales voltage measurements */
static const u16 nom_mv[] = { 2500, 2700, 3300, 5000, 12000, 2700 };
static inline unsigned int IN_FROM_REG(u8 reg, int n)
{
return SCALE(reg, nom_mv[n], 192);
}
static inline u8 IN_TO_REG(unsigned long val, int n)
{
return SENSORS_LIMIT(SCALE(val, 192, nom_mv[n]), 0, 255);
}
/* temperature range: -40..125, 127 disables temperature alarm */
static inline s8 TEMP_TO_REG(long val)
{
return SENSORS_LIMIT(SCALE(val, 1, 1000), -40, 127);
}
/* two fans, each with low fan speed limit */
static inline unsigned int FAN_FROM_REG(u8 reg, u8 div)
{
if (!reg) /* error */
return -1;
if (reg == 255)
return 0;
return SCALE(1350000, 1, reg * div);
}
/* analog out 0..1250mV */
static inline u8 AOUT_TO_REG(unsigned long val)
{
return SENSORS_LIMIT(SCALE(val, 255, 1250), 0, 255);
}
static inline unsigned int AOUT_FROM_REG(u8 reg)
{
return SCALE(reg, 1250, 255);
}
static int adm9240_attach_adapter(struct i2c_adapter *adapter);
static int adm9240_detect(struct i2c_adapter *adapter, int address, int kind);
static void adm9240_init_client(struct i2c_client *client);
static int adm9240_detach_client(struct i2c_client *client);
static struct adm9240_data *adm9240_update_device(struct device *dev);
/* driver data */
static struct i2c_driver adm9240_driver = {
.owner = THIS_MODULE,
.name = "adm9240",
.id = I2C_DRIVERID_ADM9240,
.flags = I2C_DF_NOTIFY,
.attach_adapter = adm9240_attach_adapter,
.detach_client = adm9240_detach_client,
};
/* per client data */
struct adm9240_data {
enum chips type;
struct i2c_client client;
struct semaphore update_lock;
char valid;
unsigned long last_updated_measure;
unsigned long last_updated_config;
u8 in[6]; /* ro in0_input */
u8 in_max[6]; /* rw in0_max */
u8 in_min[6]; /* rw in0_min */
u8 fan[2]; /* ro fan1_input */
u8 fan_min[2]; /* rw fan1_min */
u8 fan_div[2]; /* rw fan1_div, read-only accessor */
s16 temp; /* ro temp1_input, 9-bit sign-extended */
s8 temp_high; /* rw temp1_max */
s8 temp_hyst; /* rw temp1_max_hyst */
u16 alarms; /* ro alarms */
u8 aout; /* rw aout_output */
u8 vid; /* ro vid */
u8 vrm; /* -- vrm set on startup, no accessor */
};
/* i2c byte read/write interface */
static int adm9240_read_value(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
static int adm9240_write_value(struct i2c_client *client, u8 reg, u8 value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
/*** sysfs accessors ***/
/* temperature */
#define show_temp(value, scale) \
static ssize_t show_##value(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct adm9240_data *data = adm9240_update_device(dev); \
return sprintf(buf, "%d\n", data->value * scale); \
}
show_temp(temp_high, 1000);
show_temp(temp_hyst, 1000);
show_temp(temp, 500); /* 0.5'C per bit */
#define set_temp(value, reg) \
static ssize_t set_##value(struct device *dev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct adm9240_data *data = adm9240_update_device(dev); \
long temp = simple_strtoul(buf, NULL, 10); \
\
down(&data->update_lock); \
data->value = TEMP_TO_REG(temp); \
adm9240_write_value(client, reg, data->value); \
up(&data->update_lock); \
return count; \
}
set_temp(temp_high, ADM9240_REG_TEMP_HIGH);
set_temp(temp_hyst, ADM9240_REG_TEMP_HYST);
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
show_temp_high, set_temp_high);
static DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
show_temp_hyst, set_temp_hyst);
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
/* voltage */
static ssize_t show_in(struct device *dev, char *buf, int nr)
{
struct adm9240_data *data = adm9240_update_device(dev);
return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr], nr));
}
static ssize_t show_in_min(struct device *dev, char *buf, int nr)
{
struct adm9240_data *data = adm9240_update_device(dev);
return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr], nr));
}
static ssize_t show_in_max(struct device *dev, char *buf, int nr)
{
struct adm9240_data *data = adm9240_update_device(dev);
return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr], nr));
}
static ssize_t set_in_min(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm9240_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->in_min[nr] = IN_TO_REG(val, nr);
adm9240_write_value(client, ADM9240_REG_IN_MIN(nr), data->in_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t set_in_max(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm9240_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->in_max[nr] = IN_TO_REG(val, nr);
adm9240_write_value(client, ADM9240_REG_IN_MAX(nr), data->in_max[nr]);
up(&data->update_lock);
return count;
}
#define show_in_offset(offset) \
static ssize_t show_in##offset(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
return show_in(dev, buf, offset); \
} \
static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in##offset, NULL); \
static ssize_t show_in##offset##_min(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
return show_in_min(dev, buf, offset); \
} \
static ssize_t show_in##offset##_max(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
return show_in_max(dev, buf, offset); \
} \
static ssize_t \
set_in##offset##_min(struct device *dev, \
struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
return set_in_min(dev, buf, count, offset); \
} \
static ssize_t \
set_in##offset##_max(struct device *dev, \
struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
return set_in_max(dev, buf, count, offset); \
} \
static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
show_in##offset##_min, set_in##offset##_min); \
static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
show_in##offset##_max, set_in##offset##_max);
show_in_offset(0);
show_in_offset(1);
show_in_offset(2);
show_in_offset(3);
show_in_offset(4);
show_in_offset(5);
/* fans */
static ssize_t show_fan(struct device *dev, char *buf, int nr)
{
struct adm9240_data *data = adm9240_update_device(dev);
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
1 << data->fan_div[nr]));
}
static ssize_t show_fan_min(struct device *dev, char *buf, int nr)
{
struct adm9240_data *data = adm9240_update_device(dev);
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr],
1 << data->fan_div[nr]));
}
static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
{
struct adm9240_data *data = adm9240_update_device(dev);
return sprintf(buf, "%d\n", 1 << data->fan_div[nr]);
}
/* write new fan div, callers must hold data->update_lock */
static void adm9240_write_fan_div(struct i2c_client *client, int nr,
u8 fan_div)
{
u8 reg, old, shift = (nr + 2) * 2;
reg = adm9240_read_value(client, ADM9240_REG_VID_FAN_DIV);
old = (reg >> shift) & 3;
reg &= ~(3 << shift);
reg |= (fan_div << shift);
adm9240_write_value(client, ADM9240_REG_VID_FAN_DIV, reg);
dev_dbg(&client->dev, "fan%d clock divider changed from %u "
"to %u\n", nr + 1, 1 << old, 1 << fan_div);
}
/*
* set fan speed low limit:
*
* - value is zero: disable fan speed low limit alarm
*
* - value is below fan speed measurement range: enable fan speed low
* limit alarm to be asserted while fan speed too slow to measure
*
* - otherwise: select fan clock divider to suit fan speed low limit,
* measurement code may adjust registers to ensure fan speed reading
*/
static ssize_t set_fan_min(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm9240_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
u8 new_div;
down(&data->update_lock);
if (!val) {
data->fan_min[nr] = 255;
new_div = data->fan_div[nr];
dev_dbg(&client->dev, "fan%u low limit set disabled\n",
nr + 1);
} else if (val < 1350000 / (8 * 254)) {
new_div = 3;
data->fan_min[nr] = 254;
dev_dbg(&client->dev, "fan%u low limit set minimum %u\n",
nr + 1, FAN_FROM_REG(254, 1 << new_div));
} else {
unsigned int new_min = 1350000 / val;
new_div = 0;
while (new_min > 192 && new_div < 3) {
new_div++;
new_min /= 2;
}
if (!new_min) /* keep > 0 */
new_min++;
data->fan_min[nr] = new_min;
dev_dbg(&client->dev, "fan%u low limit set fan speed %u\n",
nr + 1, FAN_FROM_REG(new_min, 1 << new_div));
}
if (new_div != data->fan_div[nr]) {
data->fan_div[nr] = new_div;
adm9240_write_fan_div(client, nr, new_div);
}
adm9240_write_value(client, ADM9240_REG_FAN_MIN(nr),
data->fan_min[nr]);
up(&data->update_lock);
return count;
}
#define show_fan_offset(offset) \
static ssize_t show_fan_##offset (struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
return show_fan(dev, buf, offset - 1); \
} \
static ssize_t show_fan_##offset##_div (struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
return show_fan_div(dev, buf, offset - 1); \
} \
static ssize_t show_fan_##offset##_min (struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
return show_fan_min(dev, buf, offset - 1); \
} \
static ssize_t set_fan_##offset##_min (struct device *dev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_fan_min(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \
show_fan_##offset, NULL); \
static DEVICE_ATTR(fan##offset##_div, S_IRUGO, \
show_fan_##offset##_div, NULL); \
static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
show_fan_##offset##_min, set_fan_##offset##_min);
show_fan_offset(1);
show_fan_offset(2);
/* alarms */
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
{
struct adm9240_data *data = adm9240_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
/* vid */
static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf)
{
struct adm9240_data *data = adm9240_update_device(dev);
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
}
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
/* analog output */
static ssize_t show_aout(struct device *dev, struct device_attribute *attr, char *buf)
{
struct adm9240_data *data = adm9240_update_device(dev);
return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout));
}
static ssize_t set_aout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm9240_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->aout = AOUT_TO_REG(val);
adm9240_write_value(client, ADM9240_REG_ANALOG_OUT, data->aout);
up(&data->update_lock);
return count;
}
static DEVICE_ATTR(aout_output, S_IRUGO | S_IWUSR, show_aout, set_aout);
/* chassis_clear */
static ssize_t chassis_clear(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned long val = simple_strtol(buf, NULL, 10);
if (val == 1) {
adm9240_write_value(client, ADM9240_REG_CHASSIS_CLEAR, 0x80);
dev_dbg(&client->dev, "chassis intrusion latch cleared\n");
}
return count;
}
static DEVICE_ATTR(chassis_clear, S_IWUSR, NULL, chassis_clear);
/*** sensor chip detect and driver install ***/
static int adm9240_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct adm9240_data *data;
int err = 0;
const char *name = "";
u8 man_id, die_rev;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct adm9240_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct adm9240_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &adm9240_driver;
new_client->flags = 0;
if (kind == 0) {
kind = adm9240;
}
if (kind < 0) {
/* verify chip: reg address should match i2c address */
if (adm9240_read_value(new_client, ADM9240_REG_I2C_ADDR)
!= address) {
dev_err(&adapter->dev, "detect fail: address match, "
"0x%02x\n", address);
goto exit_free;
}
/* check known chip manufacturer */
man_id = adm9240_read_value(new_client, ADM9240_REG_MAN_ID);
if (man_id == 0x23) {
kind = adm9240;
} else if (man_id == 0xda) {
kind = ds1780;
} else if (man_id == 0x01) {
kind = lm81;
} else {
dev_err(&adapter->dev, "detect fail: unknown manuf, "
"0x%02x\n", man_id);
goto exit_free;
}
/* successful detect, print chip info */
die_rev = adm9240_read_value(new_client, ADM9240_REG_DIE_REV);
dev_info(&adapter->dev, "found %s revision %u\n",
man_id == 0x23 ? "ADM9240" :
man_id == 0xda ? "DS1780" : "LM81", die_rev);
}
/* either forced or detected chip kind */
if (kind == adm9240) {
name = "adm9240";
} else if (kind == ds1780) {
name = "ds1780";
} else if (kind == lm81) {
name = "lm81";
}
/* fill in the remaining client fields and attach */
strlcpy(new_client->name, name, I2C_NAME_SIZE);
data->type = kind;
init_MUTEX(&data->update_lock);
if ((err = i2c_attach_client(new_client)))
goto exit_free;
adm9240_init_client(new_client);
/* populate sysfs filesystem */
device_create_file(&new_client->dev, &dev_attr_in0_input);
device_create_file(&new_client->dev, &dev_attr_in0_min);
device_create_file(&new_client->dev, &dev_attr_in0_max);
device_create_file(&new_client->dev, &dev_attr_in1_input);
device_create_file(&new_client->dev, &dev_attr_in1_min);
device_create_file(&new_client->dev, &dev_attr_in1_max);
device_create_file(&new_client->dev, &dev_attr_in2_input);
device_create_file(&new_client->dev, &dev_attr_in2_min);
device_create_file(&new_client->dev, &dev_attr_in2_max);
device_create_file(&new_client->dev, &dev_attr_in3_input);
device_create_file(&new_client->dev, &dev_attr_in3_min);
device_create_file(&new_client->dev, &dev_attr_in3_max);
device_create_file(&new_client->dev, &dev_attr_in4_input);
device_create_file(&new_client->dev, &dev_attr_in4_min);
device_create_file(&new_client->dev, &dev_attr_in4_max);
device_create_file(&new_client->dev, &dev_attr_in5_input);
device_create_file(&new_client->dev, &dev_attr_in5_min);
device_create_file(&new_client->dev, &dev_attr_in5_max);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_fan1_input);
device_create_file(&new_client->dev, &dev_attr_fan1_div);
device_create_file(&new_client->dev, &dev_attr_fan1_min);
device_create_file(&new_client->dev, &dev_attr_fan2_input);
device_create_file(&new_client->dev, &dev_attr_fan2_div);
device_create_file(&new_client->dev, &dev_attr_fan2_min);
device_create_file(&new_client->dev, &dev_attr_alarms);
device_create_file(&new_client->dev, &dev_attr_aout_output);
device_create_file(&new_client->dev, &dev_attr_chassis_clear);
device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
return 0;
exit_free:
kfree(new_client);
exit:
return err;
}
static int adm9240_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, adm9240_detect);
}
static int adm9240_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;
}
kfree(i2c_get_clientdata(client));
return 0;
}
static void adm9240_init_client(struct i2c_client *client)
{
struct adm9240_data *data = i2c_get_clientdata(client);
u8 conf = adm9240_read_value(client, ADM9240_REG_CONFIG);
u8 mode = adm9240_read_value(client, ADM9240_REG_TEMP_CONF) & 3;
data->vrm = i2c_which_vrm(); /* need this to report vid as mV */
dev_info(&client->dev, "Using VRM: %d.%d\n", data->vrm / 10,
data->vrm % 10);
if (conf & 1) { /* measurement cycle running: report state */
dev_info(&client->dev, "status: config 0x%02x mode %u\n",
conf, mode);
} else { /* cold start: open limits before starting chip */
int i;
for (i = 0; i < 6; i++)
{
adm9240_write_value(client,
ADM9240_REG_IN_MIN(i), 0);
adm9240_write_value(client,
ADM9240_REG_IN_MAX(i), 255);
}
adm9240_write_value(client, ADM9240_REG_FAN_MIN(0), 255);
adm9240_write_value(client, ADM9240_REG_FAN_MIN(1), 255);
adm9240_write_value(client, ADM9240_REG_TEMP_HIGH, 127);
adm9240_write_value(client, ADM9240_REG_TEMP_HYST, 127);
/* start measurement cycle */
adm9240_write_value(client, ADM9240_REG_CONFIG, 1);
dev_info(&client->dev, "cold start: config was 0x%02x "
"mode %u\n", conf, mode);
}
}
static struct adm9240_data *adm9240_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct adm9240_data *data = i2c_get_clientdata(client);
int i;
down(&data->update_lock);
/* minimum measurement cycle: 1.75 seconds */
if (time_after(jiffies, data->last_updated_measure + (HZ * 7 / 4))
|| !data->valid) {
for (i = 0; i < 6; i++) /* read voltages */
{
data->in[i] = adm9240_read_value(client,
ADM9240_REG_IN(i));
}
data->alarms = adm9240_read_value(client,
ADM9240_REG_INT(0)) |
adm9240_read_value(client,
ADM9240_REG_INT(1)) << 8;
/* read temperature: assume temperature changes less than
* 0.5'C per two measurement cycles thus ignore possible
* but unlikely aliasing error on lsb reading. --Grant */
data->temp = ((adm9240_read_value(client,
ADM9240_REG_TEMP) << 8) |
adm9240_read_value(client,
ADM9240_REG_TEMP_CONF)) / 128;
for (i = 0; i < 2; i++) /* read fans */
{
data->fan[i] = adm9240_read_value(client,
ADM9240_REG_FAN(i));
/* adjust fan clock divider on overflow */
if (data->valid && data->fan[i] == 255 &&
data->fan_div[i] < 3) {
adm9240_write_fan_div(client, i,
++data->fan_div[i]);
/* adjust fan_min if active, but not to 0 */
if (data->fan_min[i] < 255 &&
data->fan_min[i] >= 2)
data->fan_min[i] /= 2;
}
}
data->last_updated_measure = jiffies;
}
/* minimum config reading cycle: 300 seconds */
if (time_after(jiffies, data->last_updated_config + (HZ * 300))
|| !data->valid) {
for (i = 0; i < 6; i++)
{
data->in_min[i] = adm9240_read_value(client,
ADM9240_REG_IN_MIN(i));
data->in_max[i] = adm9240_read_value(client,
ADM9240_REG_IN_MAX(i));
}
for (i = 0; i < 2; i++)
{
data->fan_min[i] = adm9240_read_value(client,
ADM9240_REG_FAN_MIN(i));
}
data->temp_high = adm9240_read_value(client,
ADM9240_REG_TEMP_HIGH);
data->temp_hyst = adm9240_read_value(client,
ADM9240_REG_TEMP_HYST);
/* read fan divs and 5-bit VID */
i = adm9240_read_value(client, ADM9240_REG_VID_FAN_DIV);
data->fan_div[0] = (i >> 4) & 3;
data->fan_div[1] = (i >> 6) & 3;
data->vid = i & 0x0f;
data->vid |= (adm9240_read_value(client,
ADM9240_REG_VID4) & 1) << 4;
/* read analog out */
data->aout = adm9240_read_value(client,
ADM9240_REG_ANALOG_OUT);
data->last_updated_config = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_adm9240_init(void)
{
return i2c_add_driver(&adm9240_driver);
}
static void __exit sensors_adm9240_exit(void)
{
i2c_del_driver(&adm9240_driver);
}
MODULE_AUTHOR("Michiel Rook <michiel@grendelproject.nl>, "
"Grant Coady <gcoady@gmail.com> and others");
MODULE_DESCRIPTION("ADM9240/DS1780/LM81 driver");
MODULE_LICENSE("GPL");
module_init(sensors_adm9240_init);
module_exit(sensors_adm9240_exit);

1065
drivers/hwmon/asb100.c Normal file

File diff suppressed because it is too large Load Diff

361
drivers/hwmon/atxp1.c Normal file
Näytä tiedosto

@@ -0,0 +1,361 @@
/*
atxp1.c - kernel module for setting CPU VID and general purpose
I/Os using the Attansic ATXP1 chip.
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.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/i2c-vid.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("System voltages control via Attansic ATXP1");
MODULE_VERSION("0.6.2");
MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
#define ATXP1_VID 0x00
#define ATXP1_CVID 0x01
#define ATXP1_GPIO1 0x06
#define ATXP1_GPIO2 0x0a
#define ATXP1_VIDENA 0x20
#define ATXP1_VIDMASK 0x1f
#define ATXP1_GPIO1MASK 0x0f
static unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
SENSORS_INSMOD_1(atxp1);
static int atxp1_attach_adapter(struct i2c_adapter * adapter);
static int atxp1_detach_client(struct i2c_client * client);
static struct atxp1_data * atxp1_update_device(struct device *dev);
static int atxp1_detect(struct i2c_adapter *adapter, int address, int kind);
static struct i2c_driver atxp1_driver = {
.owner = THIS_MODULE,
.name = "atxp1",
.flags = I2C_DF_NOTIFY,
.attach_adapter = atxp1_attach_adapter,
.detach_client = atxp1_detach_client,
};
struct atxp1_data {
struct i2c_client client;
struct semaphore update_lock;
unsigned long last_updated;
u8 valid;
struct {
u8 vid; /* VID output register */
u8 cpu_vid; /* VID input from CPU */
u8 gpio1; /* General purpose I/O register 1 */
u8 gpio2; /* General purpose I/O register 2 */
} reg;
u8 vrm; /* Detected CPU VRM */
};
static struct atxp1_data * atxp1_update_device(struct device *dev)
{
struct i2c_client *client;
struct atxp1_data *data;
client = to_i2c_client(dev);
data = i2c_get_clientdata(client);
down(&data->update_lock);
if ((jiffies - data->last_updated > HZ) ||
(jiffies < data->last_updated) ||
!data->valid) {
/* Update local register data */
data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID);
data->reg.cpu_vid = i2c_smbus_read_byte_data(client, ATXP1_CVID);
data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1);
data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2);
data->valid = 1;
}
up(&data->update_lock);
return(data);
}
/* sys file functions for cpu0_vid */
static ssize_t atxp1_showvcore(struct device *dev, struct device_attribute *attr, char *buf)
{
int size;
struct atxp1_data *data;
data = atxp1_update_device(dev);
size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK, data->vrm));
return size;
}
static ssize_t atxp1_storevcore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct atxp1_data *data;
struct i2c_client *client;
char vid;
char cvid;
unsigned int vcore;
client = to_i2c_client(dev);
data = atxp1_update_device(dev);
vcore = simple_strtoul(buf, NULL, 10);
vcore /= 25;
vcore *= 25;
/* Calculate VID */
vid = vid_to_reg(vcore, data->vrm);
if (vid < 0) {
dev_err(dev, "VID calculation failed.\n");
return -1;
}
/* If output enabled, use control register value. Otherwise original CPU VID */
if (data->reg.vid & ATXP1_VIDENA)
cvid = data->reg.vid & ATXP1_VIDMASK;
else
cvid = data->reg.cpu_vid;
/* Nothing changed, aborting */
if (vid == cvid)
return count;
dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", vcore, vid);
/* Write every 25 mV step to increase stability */
if (cvid > vid) {
for (; cvid >= vid; cvid--) {
i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA);
}
}
else {
for (; cvid <= vid; cvid++) {
i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA);
}
}
data->valid = 0;
return count;
}
/* CPU core reference voltage
unit: millivolt
*/
static DEVICE_ATTR(cpu0_vid, S_IRUGO | S_IWUSR, atxp1_showvcore, atxp1_storevcore);
/* sys file functions for GPIO1 */
static ssize_t atxp1_showgpio1(struct device *dev, struct device_attribute *attr, char *buf)
{
int size;
struct atxp1_data *data;
data = atxp1_update_device(dev);
size = sprintf(buf, "0x%02x\n", data->reg.gpio1 & ATXP1_GPIO1MASK);
return size;
}
static ssize_t atxp1_storegpio1(struct device *dev, struct device_attribute *attr, const char*buf, size_t count)
{
struct atxp1_data *data;
struct i2c_client *client;
unsigned int value;
client = to_i2c_client(dev);
data = atxp1_update_device(dev);
value = simple_strtoul(buf, NULL, 16);
value &= ATXP1_GPIO1MASK;
if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) {
dev_info(dev, "Writing 0x%x to GPIO1.\n", value);
i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value);
data->valid = 0;
}
return count;
}
/* GPIO1 data register
unit: Four bit as hex (e.g. 0x0f)
*/
static DEVICE_ATTR(gpio1, S_IRUGO | S_IWUSR, atxp1_showgpio1, atxp1_storegpio1);
/* sys file functions for GPIO2 */
static ssize_t atxp1_showgpio2(struct device *dev, struct device_attribute *attr, char *buf)
{
int size;
struct atxp1_data *data;
data = atxp1_update_device(dev);
size = sprintf(buf, "0x%02x\n", data->reg.gpio2);
return size;
}
static ssize_t atxp1_storegpio2(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct atxp1_data *data;
struct i2c_client *client;
unsigned int value;
client = to_i2c_client(dev);
data = atxp1_update_device(dev);
value = simple_strtoul(buf, NULL, 16) & 0xff;
if (value != data->reg.gpio2) {
dev_info(dev, "Writing 0x%x to GPIO1.\n", value);
i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value);
data->valid = 0;
}
return count;
}
/* GPIO2 data register
unit: Eight bit as hex (e.g. 0xff)
*/
static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2);
static int atxp1_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_detect(adapter, &addr_data, &atxp1_detect);
};
static int atxp1_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client * new_client;
struct atxp1_data * data;
int err = 0;
u8 temp;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct atxp1_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct atxp1_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &atxp1_driver;
new_client->flags = 0;
/* Detect ATXP1, checking if vendor ID registers are all zero */
if (!((i2c_smbus_read_byte_data(new_client, 0x3e) == 0) &&
(i2c_smbus_read_byte_data(new_client, 0x3f) == 0) &&
(i2c_smbus_read_byte_data(new_client, 0xfe) == 0) &&
(i2c_smbus_read_byte_data(new_client, 0xff) == 0) )) {
/* No vendor ID, now checking if registers 0x10,0x11 (non-existent)
* showing the same as register 0x00 */
temp = i2c_smbus_read_byte_data(new_client, 0x00);
if (!((i2c_smbus_read_byte_data(new_client, 0x10) == temp) &&
(i2c_smbus_read_byte_data(new_client, 0x11) == temp) ))
goto exit_free;
}
/* Get VRM */
data->vrm = i2c_which_vrm();
if ((data->vrm != 90) && (data->vrm != 91)) {
dev_err(&new_client->dev, "Not supporting VRM %d.%d\n",
data->vrm / 10, data->vrm % 10);
goto exit_free;
}
strncpy(new_client->name, "atxp1", I2C_NAME_SIZE);
data->valid = 0;
init_MUTEX(&data->update_lock);
err = i2c_attach_client(new_client);
if (err)
{
dev_err(&new_client->dev, "Attach client error.\n");
goto exit_free;
}
device_create_file(&new_client->dev, &dev_attr_gpio1);
device_create_file(&new_client->dev, &dev_attr_gpio2);
device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
dev_info(&new_client->dev, "Using VRM: %d.%d\n",
data->vrm / 10, data->vrm % 10);
return 0;
exit_free:
kfree(data);
exit:
return err;
};
static int atxp1_detach_client(struct i2c_client * client)
{
int err;
err = i2c_detach_client(client);
if (err)
dev_err(&client->dev, "Failed to detach client.\n");
else
kfree(i2c_get_clientdata(client));
return err;
};
static int __init atxp1_init(void)
{
return i2c_add_driver(&atxp1_driver);
};
static void __exit atxp1_exit(void)
{
i2c_del_driver(&atxp1_driver);
};
module_init(atxp1_init);
module_exit(atxp1_exit);

341
drivers/hwmon/ds1621.c Normal file
Näytä tiedosto

@@ -0,0 +1,341 @@
/*
ds1621.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Christian W. Zuckschwerdt <zany@triq.net> 2000-11-23
based on lm75.c by Frodo Looijaard <frodol@dds.nl>
Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with
the help of Jean Delvare <khali@linux-fr.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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include "lm75.h"
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(ds1621);
static int polarity = -1;
module_param(polarity, int, 0);
MODULE_PARM_DESC(polarity, "Output's polarity: 0 = active high, 1 = active low");
/* Many DS1621 constants specified below */
/* Config register used for detection */
/* 7 6 5 4 3 2 1 0 */
/* |Done|THF |TLF |NVB | X | X |POL |1SHOT| */
#define DS1621_REG_CONFIG_NVB 0x10
#define DS1621_REG_CONFIG_POLARITY 0x02
#define DS1621_REG_CONFIG_1SHOT 0x01
#define DS1621_REG_CONFIG_DONE 0x80
/* The DS1621 registers */
#define DS1621_REG_TEMP 0xAA /* word, RO */
#define DS1621_REG_TEMP_MIN 0xA1 /* word, RW */
#define DS1621_REG_TEMP_MAX 0xA2 /* word, RW */
#define DS1621_REG_CONF 0xAC /* byte, RW */
#define DS1621_COM_START 0xEE /* no data */
#define DS1621_COM_STOP 0x22 /* no data */
/* The DS1621 configuration register */
#define DS1621_ALARM_TEMP_HIGH 0x40
#define DS1621_ALARM_TEMP_LOW 0x20
/* 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 ALARMS_FROM_REG(val) ((val) & \
(DS1621_ALARM_TEMP_HIGH | DS1621_ALARM_TEMP_LOW))
/* Each client has this additional data */
struct ds1621_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u16 temp, temp_min, temp_max; /* Register values, word */
u8 conf; /* Register encoding, combined */
};
static int ds1621_attach_adapter(struct i2c_adapter *adapter);
static int ds1621_detect(struct i2c_adapter *adapter, int address,
int kind);
static void ds1621_init_client(struct i2c_client *client);
static int ds1621_detach_client(struct i2c_client *client);
static struct ds1621_data *ds1621_update_client(struct device *dev);
/* This is the driver that will be inserted */
static struct i2c_driver ds1621_driver = {
.owner = THIS_MODULE,
.name = "ds1621",
.id = I2C_DRIVERID_DS1621,
.flags = I2C_DF_NOTIFY,
.attach_adapter = ds1621_attach_adapter,
.detach_client = ds1621_detach_client,
};
/* All registers are word-sized, except for the configuration register.
DS1621 uses a high-byte first convention, which is exactly opposite to
the usual practice. */
static int ds1621_read_value(struct i2c_client *client, u8 reg)
{
if (reg == DS1621_REG_CONF)
return i2c_smbus_read_byte_data(client, reg);
else
return swab16(i2c_smbus_read_word_data(client, reg));
}
/* All registers are word-sized, except for the configuration register.
DS1621 uses a high-byte first convention, which is exactly opposite to
the usual practice. */
static int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if (reg == DS1621_REG_CONF)
return i2c_smbus_write_byte_data(client, reg, value);
else
return i2c_smbus_write_word_data(client, reg, swab16(value));
}
static void ds1621_init_client(struct i2c_client *client)
{
int reg = ds1621_read_value(client, DS1621_REG_CONF);
/* switch to continuous conversion mode */
reg &= ~ DS1621_REG_CONFIG_1SHOT;
/* setup output polarity */
if (polarity == 0)
reg &= ~DS1621_REG_CONFIG_POLARITY;
else if (polarity == 1)
reg |= DS1621_REG_CONFIG_POLARITY;
ds1621_write_value(client, DS1621_REG_CONF, reg);
/* start conversion */
i2c_smbus_write_byte(client, DS1621_COM_START);
}
#define show(value) \
static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct ds1621_data *data = ds1621_update_client(dev); \
return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->value)); \
}
show(temp);
show(temp_min);
show(temp_max);
#define set_temp(suffix, value, reg) \
static ssize_t set_temp_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct ds1621_data *data = ds1621_update_client(dev); \
u16 val = LM75_TEMP_TO_REG(simple_strtoul(buf, NULL, 10)); \
\
down(&data->update_lock); \
data->value = val; \
ds1621_write_value(client, reg, data->value); \
up(&data->update_lock); \
return count; \
}
set_temp(min, temp_min, DS1621_REG_TEMP_MIN);
set_temp(max, temp_max, DS1621_REG_TEMP_MAX);
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
{
struct ds1621_data *data = ds1621_update_client(dev);
return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->conf));
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
static DEVICE_ATTR(temp1_input, S_IRUGO , show_temp, NULL);
static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO , show_temp_min, set_temp_min);
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max);
static int ds1621_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_detect(adapter, &addr_data, ds1621_detect);
}
/* This function is called by i2c_detect */
int ds1621_detect(struct i2c_adapter *adapter, int address,
int kind)
{
int conf, temp;
struct i2c_client *new_client;
struct ds1621_data *data;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
| I2C_FUNC_SMBUS_WORD_DATA
| I2C_FUNC_SMBUS_WRITE_BYTE))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
But it allows us to access ds1621_{read,write}_value. */
if (!(data = kmalloc(sizeof(struct ds1621_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct ds1621_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &ds1621_driver;
new_client->flags = 0;
/* Now, we do the remaining detection. It is lousy. */
if (kind < 0) {
/* The NVB bit should be low if no EEPROM write has been
requested during the latest 10ms, which is highly
improbable in our case. */
conf = ds1621_read_value(new_client, DS1621_REG_CONF);
if (conf & DS1621_REG_CONFIG_NVB)
goto exit_free;
/* The 7 lowest bits of a temperature should always be 0. */
temp = ds1621_read_value(new_client, DS1621_REG_TEMP);
if (temp & 0x007f)
goto exit_free;
temp = ds1621_read_value(new_client, DS1621_REG_TEMP_MIN);
if (temp & 0x007f)
goto exit_free;
temp = ds1621_read_value(new_client, DS1621_REG_TEMP_MAX);
if (temp & 0x007f)
goto exit_free;
}
/* Determine the chip type - only one kind supported! */
if (kind <= 0)
kind = ds1621;
/* Fill in remaining client fields and put it into the global list */
strlcpy(new_client->name, "ds1621", I2C_NAME_SIZE);
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 exit_free;
/* Initialize the DS1621 chip */
ds1621_init_client(new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_alarms);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_min);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
return 0;
/* OK, this is not exactly good programming practice, usually. But it is
very code-efficient in this case. */
exit_free:
kfree(data);
exit:
return err;
}
static int ds1621_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;
}
kfree(i2c_get_clientdata(client));
return 0;
}
static struct ds1621_data *ds1621_update_client(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct ds1621_data *data = i2c_get_clientdata(client);
u8 new_conf;
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
dev_dbg(&client->dev, "Starting ds1621 update\n");
data->conf = ds1621_read_value(client, DS1621_REG_CONF);
data->temp = ds1621_read_value(client, DS1621_REG_TEMP);
data->temp_min = ds1621_read_value(client,
DS1621_REG_TEMP_MIN);
data->temp_max = ds1621_read_value(client,
DS1621_REG_TEMP_MAX);
/* reset alarms if necessary */
new_conf = data->conf;
if (data->temp < data->temp_min)
new_conf &= ~DS1621_ALARM_TEMP_LOW;
if (data->temp > data->temp_max)
new_conf &= ~DS1621_ALARM_TEMP_HIGH;
if (data->conf != new_conf)
ds1621_write_value(client, DS1621_REG_CONF,
new_conf);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init ds1621_init(void)
{
return i2c_add_driver(&ds1621_driver);
}
static void __exit ds1621_exit(void)
{
i2c_del_driver(&ds1621_driver);
}
MODULE_AUTHOR("Christian W. Zuckschwerdt <zany@triq.net>");
MODULE_DESCRIPTION("DS1621 driver");
MODULE_LICENSE("GPL");
module_init(ds1621_init);
module_exit(ds1621_exit);

691
drivers/hwmon/fscher.c Normal file
Näytä tiedosto

@@ -0,0 +1,691 @@
/*
* fscher.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
* Copyright (C) 2003, 2004 Reinhard Nissl <rnissl@gmx.de>
*
* 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.
*/
/*
* fujitsu siemens hermes chip,
* module based on fscpos.c
* Copyright (C) 2000 Hermann Jung <hej@odn.de>
* Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
* and Philip Edelbrock <phil@netroedge.com>
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/*
* Addresses to scan
*/
static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_1(fscher);
/*
* The FSCHER registers
*/
/* chip identification */
#define FSCHER_REG_IDENT_0 0x00
#define FSCHER_REG_IDENT_1 0x01
#define FSCHER_REG_IDENT_2 0x02
#define FSCHER_REG_REVISION 0x03
/* global control and status */
#define FSCHER_REG_EVENT_STATE 0x04
#define FSCHER_REG_CONTROL 0x05
/* watchdog */
#define FSCHER_REG_WDOG_PRESET 0x28
#define FSCHER_REG_WDOG_STATE 0x23
#define FSCHER_REG_WDOG_CONTROL 0x21
/* fan 0 */
#define FSCHER_REG_FAN0_MIN 0x55
#define FSCHER_REG_FAN0_ACT 0x0e
#define FSCHER_REG_FAN0_STATE 0x0d
#define FSCHER_REG_FAN0_RIPPLE 0x0f
/* fan 1 */
#define FSCHER_REG_FAN1_MIN 0x65
#define FSCHER_REG_FAN1_ACT 0x6b
#define FSCHER_REG_FAN1_STATE 0x62
#define FSCHER_REG_FAN1_RIPPLE 0x6f
/* fan 2 */
#define FSCHER_REG_FAN2_MIN 0xb5
#define FSCHER_REG_FAN2_ACT 0xbb
#define FSCHER_REG_FAN2_STATE 0xb2
#define FSCHER_REG_FAN2_RIPPLE 0xbf
/* voltage supervision */
#define FSCHER_REG_VOLT_12 0x45
#define FSCHER_REG_VOLT_5 0x42
#define FSCHER_REG_VOLT_BATT 0x48
/* temperature 0 */
#define FSCHER_REG_TEMP0_ACT 0x64
#define FSCHER_REG_TEMP0_STATE 0x71
/* temperature 1 */
#define FSCHER_REG_TEMP1_ACT 0x32
#define FSCHER_REG_TEMP1_STATE 0x81
/* temperature 2 */
#define FSCHER_REG_TEMP2_ACT 0x35
#define FSCHER_REG_TEMP2_STATE 0x91
/*
* Functions declaration
*/
static int fscher_attach_adapter(struct i2c_adapter *adapter);
static int fscher_detect(struct i2c_adapter *adapter, int address, int kind);
static int fscher_detach_client(struct i2c_client *client);
static struct fscher_data *fscher_update_device(struct device *dev);
static void fscher_init_client(struct i2c_client *client);
static int fscher_read_value(struct i2c_client *client, u8 reg);
static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver fscher_driver = {
.owner = THIS_MODULE,
.name = "fscher",
.id = I2C_DRIVERID_FSCHER,
.flags = I2C_DF_NOTIFY,
.attach_adapter = fscher_attach_adapter,
.detach_client = fscher_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct fscher_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
/* register values */
u8 revision; /* revision of chip */
u8 global_event; /* global event status */
u8 global_control; /* global control register */
u8 watchdog[3]; /* watchdog */
u8 volt[3]; /* 12, 5, battery voltage */
u8 temp_act[3]; /* temperature */
u8 temp_status[3]; /* status of sensor */
u8 fan_act[3]; /* fans revolutions per second */
u8 fan_status[3]; /* fan status */
u8 fan_min[3]; /* fan min value for rps */
u8 fan_ripple[3]; /* divider for rps */
};
/*
* Sysfs stuff
*/
#define sysfs_r(kind, sub, offset, reg) \
static ssize_t show_##kind##sub (struct fscher_data *, char *, int); \
static ssize_t show_##kind##offset##sub (struct device *, struct device_attribute *attr, char *); \
static ssize_t show_##kind##offset##sub (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct fscher_data *data = fscher_update_device(dev); \
return show_##kind##sub(data, buf, (offset)); \
}
#define sysfs_w(kind, sub, offset, reg) \
static ssize_t set_##kind##sub (struct i2c_client *, struct fscher_data *, const char *, size_t, int, int); \
static ssize_t set_##kind##offset##sub (struct device *, struct device_attribute *attr, const char *, size_t); \
static ssize_t set_##kind##offset##sub (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct fscher_data *data = i2c_get_clientdata(client); \
return set_##kind##sub(client, data, buf, count, (offset), reg); \
}
#define sysfs_rw_n(kind, sub, offset, reg) \
sysfs_r(kind, sub, offset, reg) \
sysfs_w(kind, sub, offset, reg) \
static DEVICE_ATTR(kind##offset##sub, S_IRUGO | S_IWUSR, show_##kind##offset##sub, set_##kind##offset##sub);
#define sysfs_rw(kind, sub, reg) \
sysfs_r(kind, sub, 0, reg) \
sysfs_w(kind, sub, 0, reg) \
static DEVICE_ATTR(kind##sub, S_IRUGO | S_IWUSR, show_##kind##0##sub, set_##kind##0##sub);
#define sysfs_ro_n(kind, sub, offset, reg) \
sysfs_r(kind, sub, offset, reg) \
static DEVICE_ATTR(kind##offset##sub, S_IRUGO, show_##kind##offset##sub, NULL);
#define sysfs_ro(kind, sub, reg) \
sysfs_r(kind, sub, 0, reg) \
static DEVICE_ATTR(kind, S_IRUGO, show_##kind##0##sub, NULL);
#define sysfs_fan(offset, reg_status, reg_min, reg_ripple, reg_act) \
sysfs_rw_n(pwm, , offset, reg_min) \
sysfs_rw_n(fan, _status, offset, reg_status) \
sysfs_rw_n(fan, _div , offset, reg_ripple) \
sysfs_ro_n(fan, _input , offset, reg_act)
#define sysfs_temp(offset, reg_status, reg_act) \
sysfs_rw_n(temp, _status, offset, reg_status) \
sysfs_ro_n(temp, _input , offset, reg_act)
#define sysfs_in(offset, reg_act) \
sysfs_ro_n(in, _input, offset, reg_act)
#define sysfs_revision(reg_revision) \
sysfs_ro(revision, , reg_revision)
#define sysfs_alarms(reg_events) \
sysfs_ro(alarms, , reg_events)
#define sysfs_control(reg_control) \
sysfs_rw(control, , reg_control)
#define sysfs_watchdog(reg_control, reg_status, reg_preset) \
sysfs_rw(watchdog, _control, reg_control) \
sysfs_rw(watchdog, _status , reg_status) \
sysfs_rw(watchdog, _preset , reg_preset)
sysfs_fan(1, FSCHER_REG_FAN0_STATE, FSCHER_REG_FAN0_MIN,
FSCHER_REG_FAN0_RIPPLE, FSCHER_REG_FAN0_ACT)
sysfs_fan(2, FSCHER_REG_FAN1_STATE, FSCHER_REG_FAN1_MIN,
FSCHER_REG_FAN1_RIPPLE, FSCHER_REG_FAN1_ACT)
sysfs_fan(3, FSCHER_REG_FAN2_STATE, FSCHER_REG_FAN2_MIN,
FSCHER_REG_FAN2_RIPPLE, FSCHER_REG_FAN2_ACT)
sysfs_temp(1, FSCHER_REG_TEMP0_STATE, FSCHER_REG_TEMP0_ACT)
sysfs_temp(2, FSCHER_REG_TEMP1_STATE, FSCHER_REG_TEMP1_ACT)
sysfs_temp(3, FSCHER_REG_TEMP2_STATE, FSCHER_REG_TEMP2_ACT)
sysfs_in(0, FSCHER_REG_VOLT_12)
sysfs_in(1, FSCHER_REG_VOLT_5)
sysfs_in(2, FSCHER_REG_VOLT_BATT)
sysfs_revision(FSCHER_REG_REVISION)
sysfs_alarms(FSCHER_REG_EVENTS)
sysfs_control(FSCHER_REG_CONTROL)
sysfs_watchdog(FSCHER_REG_WDOG_CONTROL, FSCHER_REG_WDOG_STATE, FSCHER_REG_WDOG_PRESET)
#define device_create_file_fan(client, offset) \
do { \
device_create_file(&client->dev, &dev_attr_fan##offset##_status); \
device_create_file(&client->dev, &dev_attr_pwm##offset); \
device_create_file(&client->dev, &dev_attr_fan##offset##_div); \
device_create_file(&client->dev, &dev_attr_fan##offset##_input); \
} while (0)
#define device_create_file_temp(client, offset) \
do { \
device_create_file(&client->dev, &dev_attr_temp##offset##_status); \
device_create_file(&client->dev, &dev_attr_temp##offset##_input); \
} while (0)
#define device_create_file_in(client, offset) \
do { \
device_create_file(&client->dev, &dev_attr_in##offset##_input); \
} while (0)
#define device_create_file_revision(client) \
do { \
device_create_file(&client->dev, &dev_attr_revision); \
} while (0)
#define device_create_file_alarms(client) \
do { \
device_create_file(&client->dev, &dev_attr_alarms); \
} while (0)
#define device_create_file_control(client) \
do { \
device_create_file(&client->dev, &dev_attr_control); \
} while (0)
#define device_create_file_watchdog(client) \
do { \
device_create_file(&client->dev, &dev_attr_watchdog_status); \
device_create_file(&client->dev, &dev_attr_watchdog_control); \
device_create_file(&client->dev, &dev_attr_watchdog_preset); \
} while (0)
/*
* Real code
*/
static int fscher_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, fscher_detect);
}
static int fscher_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct fscher_data *data;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
* client structure, even though we cannot fill it completely yet.
* But it allows us to access i2c_smbus_read_byte_data. */
if (!(data = kmalloc(sizeof(struct fscher_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct fscher_data));
/* The common I2C client data is placed right before the
* Hermes-specific data. */
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &fscher_driver;
new_client->flags = 0;
/* Do the remaining detection unless force or force_fscher parameter */
if (kind < 0) {
if ((i2c_smbus_read_byte_data(new_client,
FSCHER_REG_IDENT_0) != 0x48) /* 'H' */
|| (i2c_smbus_read_byte_data(new_client,
FSCHER_REG_IDENT_1) != 0x45) /* 'E' */
|| (i2c_smbus_read_byte_data(new_client,
FSCHER_REG_IDENT_2) != 0x52)) /* 'R' */
goto exit_free;
}
/* Fill in the remaining client fields and put it into the
* global list */
strlcpy(new_client->name, "fscher", I2C_NAME_SIZE);
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 exit_free;
fscher_init_client(new_client);
/* Register sysfs hooks */
device_create_file_revision(new_client);
device_create_file_alarms(new_client);
device_create_file_control(new_client);
device_create_file_watchdog(new_client);
device_create_file_in(new_client, 0);
device_create_file_in(new_client, 1);
device_create_file_in(new_client, 2);
device_create_file_fan(new_client, 1);
device_create_file_fan(new_client, 2);
device_create_file_fan(new_client, 3);
device_create_file_temp(new_client, 1);
device_create_file_temp(new_client, 2);
device_create_file_temp(new_client, 3);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static int fscher_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;
}
kfree(i2c_get_clientdata(client));
return 0;
}
static int fscher_read_value(struct i2c_client *client, u8 reg)
{
dev_dbg(&client->dev, "read reg 0x%02x\n", reg);
return i2c_smbus_read_byte_data(client, reg);
}
static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value)
{
dev_dbg(&client->dev, "write reg 0x%02x, val 0x%02x\n",
reg, value);
return i2c_smbus_write_byte_data(client, reg, value);
}
/* Called when we have found a new FSC Hermes. */
static void fscher_init_client(struct i2c_client *client)
{
struct fscher_data *data = i2c_get_clientdata(client);
/* Read revision from chip */
data->revision = fscher_read_value(client, FSCHER_REG_REVISION);
}
static struct fscher_data *fscher_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct fscher_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) {
dev_dbg(&client->dev, "Starting fscher update\n");
data->temp_act[0] = fscher_read_value(client, FSCHER_REG_TEMP0_ACT);
data->temp_act[1] = fscher_read_value(client, FSCHER_REG_TEMP1_ACT);
data->temp_act[2] = fscher_read_value(client, FSCHER_REG_TEMP2_ACT);
data->temp_status[0] = fscher_read_value(client, FSCHER_REG_TEMP0_STATE);
data->temp_status[1] = fscher_read_value(client, FSCHER_REG_TEMP1_STATE);
data->temp_status[2] = fscher_read_value(client, FSCHER_REG_TEMP2_STATE);
data->volt[0] = fscher_read_value(client, FSCHER_REG_VOLT_12);
data->volt[1] = fscher_read_value(client, FSCHER_REG_VOLT_5);
data->volt[2] = fscher_read_value(client, FSCHER_REG_VOLT_BATT);
data->fan_act[0] = fscher_read_value(client, FSCHER_REG_FAN0_ACT);
data->fan_act[1] = fscher_read_value(client, FSCHER_REG_FAN1_ACT);
data->fan_act[2] = fscher_read_value(client, FSCHER_REG_FAN2_ACT);
data->fan_status[0] = fscher_read_value(client, FSCHER_REG_FAN0_STATE);
data->fan_status[1] = fscher_read_value(client, FSCHER_REG_FAN1_STATE);
data->fan_status[2] = fscher_read_value(client, FSCHER_REG_FAN2_STATE);
data->fan_min[0] = fscher_read_value(client, FSCHER_REG_FAN0_MIN);
data->fan_min[1] = fscher_read_value(client, FSCHER_REG_FAN1_MIN);
data->fan_min[2] = fscher_read_value(client, FSCHER_REG_FAN2_MIN);
data->fan_ripple[0] = fscher_read_value(client, FSCHER_REG_FAN0_RIPPLE);
data->fan_ripple[1] = fscher_read_value(client, FSCHER_REG_FAN1_RIPPLE);
data->fan_ripple[2] = fscher_read_value(client, FSCHER_REG_FAN2_RIPPLE);
data->watchdog[0] = fscher_read_value(client, FSCHER_REG_WDOG_PRESET);
data->watchdog[1] = fscher_read_value(client, FSCHER_REG_WDOG_STATE);
data->watchdog[2] = fscher_read_value(client, FSCHER_REG_WDOG_CONTROL);
data->global_event = fscher_read_value(client, FSCHER_REG_EVENT_STATE);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
#define FAN_INDEX_FROM_NUM(nr) ((nr) - 1)
static ssize_t set_fan_status(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
/* bits 0..1, 3..7 reserved => mask with 0x04 */
unsigned long v = simple_strtoul(buf, NULL, 10) & 0x04;
down(&data->update_lock);
data->fan_status[FAN_INDEX_FROM_NUM(nr)] &= ~v;
fscher_write_value(client, reg, v);
up(&data->update_lock);
return count;
}
static ssize_t show_fan_status(struct fscher_data *data, char *buf, int nr)
{
/* bits 0..1, 3..7 reserved => mask with 0x04 */
return sprintf(buf, "%u\n", data->fan_status[FAN_INDEX_FROM_NUM(nr)] & 0x04);
}
static ssize_t set_pwm(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
unsigned long v = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->fan_min[FAN_INDEX_FROM_NUM(nr)] = v > 0xff ? 0xff : v;
fscher_write_value(client, reg, data->fan_min[FAN_INDEX_FROM_NUM(nr)]);
up(&data->update_lock);
return count;
}
static ssize_t show_pwm(struct fscher_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", data->fan_min[FAN_INDEX_FROM_NUM(nr)]);
}
static ssize_t set_fan_div(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
/* supported values: 2, 4, 8 */
unsigned long v = simple_strtoul(buf, NULL, 10);
switch (v) {
case 2: v = 1; break;
case 4: v = 2; break;
case 8: v = 3; break;
default:
dev_err(&client->dev, "fan_div value %ld not "
"supported. Choose one of 2, 4 or 8!\n", v);
return -EINVAL;
}
down(&data->update_lock);
/* bits 2..7 reserved => mask with 0x03 */
data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] &= ~0x03;
data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] |= v;
fscher_write_value(client, reg, data->fan_ripple[FAN_INDEX_FROM_NUM(nr)]);
up(&data->update_lock);
return count;
}
static ssize_t show_fan_div(struct fscher_data *data, char *buf, int nr)
{
/* bits 2..7 reserved => mask with 0x03 */
return sprintf(buf, "%u\n", 1 << (data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] & 0x03));
}
#define RPM_FROM_REG(val) (val*60)
static ssize_t show_fan_input (struct fscher_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[FAN_INDEX_FROM_NUM(nr)]));
}
#define TEMP_INDEX_FROM_NUM(nr) ((nr) - 1)
static ssize_t set_temp_status(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
/* bits 2..7 reserved, 0 read only => mask with 0x02 */
unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02;
down(&data->update_lock);
data->temp_status[TEMP_INDEX_FROM_NUM(nr)] &= ~v;
fscher_write_value(client, reg, v);
up(&data->update_lock);
return count;
}
static ssize_t show_temp_status(struct fscher_data *data, char *buf, int nr)
{
/* bits 2..7 reserved => mask with 0x03 */
return sprintf(buf, "%u\n", data->temp_status[TEMP_INDEX_FROM_NUM(nr)] & 0x03);
}
#define TEMP_FROM_REG(val) (((val) - 128) * 1000)
static ssize_t show_temp_input(struct fscher_data *data, char *buf, int nr)
{
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[TEMP_INDEX_FROM_NUM(nr)]));
}
/*
* The final conversion is specified in sensors.conf, as it depends on
* mainboard specific values. We export the registers contents as
* pseudo-hundredths-of-Volts (range 0V - 2.55V). Not that it makes much
* sense per se, but it minimizes the conversions count and keeps the
* values within a usual range.
*/
#define VOLT_FROM_REG(val) ((val) * 10)
static ssize_t show_in_input(struct fscher_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[nr]));
}
static ssize_t show_revision(struct fscher_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", data->revision);
}
static ssize_t show_alarms(struct fscher_data *data, char *buf, int nr)
{
/* bits 2, 5..6 reserved => mask with 0x9b */
return sprintf(buf, "%u\n", data->global_event & 0x9b);
}
static ssize_t set_control(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
/* bits 1..7 reserved => mask with 0x01 */
unsigned long v = simple_strtoul(buf, NULL, 10) & 0x01;
down(&data->update_lock);
data->global_control &= ~v;
fscher_write_value(client, reg, v);
up(&data->update_lock);
return count;
}
static ssize_t show_control(struct fscher_data *data, char *buf, int nr)
{
/* bits 1..7 reserved => mask with 0x01 */
return sprintf(buf, "%u\n", data->global_control & 0x01);
}
static ssize_t set_watchdog_control(struct i2c_client *client, struct
fscher_data *data, const char *buf, size_t count,
int nr, int reg)
{
/* bits 0..3 reserved => mask with 0xf0 */
unsigned long v = simple_strtoul(buf, NULL, 10) & 0xf0;
down(&data->update_lock);
data->watchdog[2] &= ~0xf0;
data->watchdog[2] |= v;
fscher_write_value(client, reg, data->watchdog[2]);
up(&data->update_lock);
return count;
}
static ssize_t show_watchdog_control(struct fscher_data *data, char *buf, int nr)
{
/* bits 0..3 reserved, bit 5 write only => mask with 0xd0 */
return sprintf(buf, "%u\n", data->watchdog[2] & 0xd0);
}
static ssize_t set_watchdog_status(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
/* bits 0, 2..7 reserved => mask with 0x02 */
unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02;
down(&data->update_lock);
data->watchdog[1] &= ~v;
fscher_write_value(client, reg, v);
up(&data->update_lock);
return count;
}
static ssize_t show_watchdog_status(struct fscher_data *data, char *buf, int nr)
{
/* bits 0, 2..7 reserved => mask with 0x02 */
return sprintf(buf, "%u\n", data->watchdog[1] & 0x02);
}
static ssize_t set_watchdog_preset(struct i2c_client *client, struct fscher_data *data,
const char *buf, size_t count, int nr, int reg)
{
unsigned long v = simple_strtoul(buf, NULL, 10) & 0xff;
down(&data->update_lock);
data->watchdog[0] = v;
fscher_write_value(client, reg, data->watchdog[0]);
up(&data->update_lock);
return count;
}
static ssize_t show_watchdog_preset(struct fscher_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", data->watchdog[0]);
}
static int __init sensors_fscher_init(void)
{
return i2c_add_driver(&fscher_driver);
}
static void __exit sensors_fscher_exit(void)
{
i2c_del_driver(&fscher_driver);
}
MODULE_AUTHOR("Reinhard Nissl <rnissl@gmx.de>");
MODULE_DESCRIPTION("FSC Hermes driver");
MODULE_LICENSE("GPL");
module_init(sensors_fscher_init);
module_exit(sensors_fscher_exit);

641
drivers/hwmon/fscpos.c Normal file
Näytä tiedosto

@@ -0,0 +1,641 @@
/*
fscpos.c - Kernel module for hardware monitoring with FSC Poseidon chips
Copyright (C) 2004, 2005 Stefan Ott <stefan@desire.ch>
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.
*/
/*
fujitsu siemens poseidon chip,
module based on the old fscpos module by Hermann Jung <hej@odn.de> and
the fscher module by Reinhard Nissl <rnissl@gmx.de>
original module based on lm80.c
Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
and Philip Edelbrock <phil@netroedge.com>
Thanks to Jean Delvare for reviewing my code and suggesting a lot of
improvements.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/init.h>
/*
* Addresses to scan
*/
static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_1(fscpos);
/*
* The FSCPOS registers
*/
/* chip identification */
#define FSCPOS_REG_IDENT_0 0x00
#define FSCPOS_REG_IDENT_1 0x01
#define FSCPOS_REG_IDENT_2 0x02
#define FSCPOS_REG_REVISION 0x03
/* global control and status */
#define FSCPOS_REG_EVENT_STATE 0x04
#define FSCPOS_REG_CONTROL 0x05
/* watchdog */
#define FSCPOS_REG_WDOG_PRESET 0x28
#define FSCPOS_REG_WDOG_STATE 0x23
#define FSCPOS_REG_WDOG_CONTROL 0x21
/* voltages */
#define FSCPOS_REG_VOLT_12 0x45
#define FSCPOS_REG_VOLT_5 0x42
#define FSCPOS_REG_VOLT_BATT 0x48
/* fans - the chip does not support minimum speed for fan2 */
static u8 FSCPOS_REG_PWM[] = { 0x55, 0x65 };
static u8 FSCPOS_REG_FAN_ACT[] = { 0x0e, 0x6b, 0xab };
static u8 FSCPOS_REG_FAN_STATE[] = { 0x0d, 0x62, 0xa2 };
static u8 FSCPOS_REG_FAN_RIPPLE[] = { 0x0f, 0x6f, 0xaf };
/* temperatures */
static u8 FSCPOS_REG_TEMP_ACT[] = { 0x64, 0x32, 0x35 };
static u8 FSCPOS_REG_TEMP_STATE[] = { 0x71, 0x81, 0x91 };
/*
* Functions declaration
*/
static int fscpos_attach_adapter(struct i2c_adapter *adapter);
static int fscpos_detect(struct i2c_adapter *adapter, int address, int kind);
static int fscpos_detach_client(struct i2c_client *client);
static int fscpos_read_value(struct i2c_client *client, u8 register);
static int fscpos_write_value(struct i2c_client *client, u8 register, u8 value);
static struct fscpos_data *fscpos_update_device(struct device *dev);
static void fscpos_init_client(struct i2c_client *client);
static void reset_fan_alarm(struct i2c_client *client, int nr);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver fscpos_driver = {
.owner = THIS_MODULE,
.name = "fscpos",
.id = I2C_DRIVERID_FSCPOS,
.flags = I2C_DF_NOTIFY,
.attach_adapter = fscpos_attach_adapter,
.detach_client = fscpos_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct fscpos_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* 0 until following fields are valid */
unsigned long last_updated; /* In jiffies */
/* register values */
u8 revision; /* revision of chip */
u8 global_event; /* global event status */
u8 global_control; /* global control register */
u8 wdog_control; /* watchdog control */
u8 wdog_state; /* watchdog status */
u8 wdog_preset; /* watchdog preset */
u8 volt[3]; /* 12, 5, battery current */
u8 temp_act[3]; /* temperature */
u8 temp_status[3]; /* status of sensor */
u8 fan_act[3]; /* fans revolutions per second */
u8 fan_status[3]; /* fan status */
u8 pwm[2]; /* fan min value for rps */
u8 fan_ripple[3]; /* divider for rps */
};
/* Temperature */
#define TEMP_FROM_REG(val) (((val) - 128) * 1000)
static ssize_t show_temp_input(struct fscpos_data *data, char *buf, int nr)
{
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[nr - 1]));
}
static ssize_t show_temp_status(struct fscpos_data *data, char *buf, int nr)
{
/* bits 2..7 reserved => mask with 0x03 */
return sprintf(buf, "%u\n", data->temp_status[nr - 1] & 0x03);
}
static ssize_t show_temp_reset(struct fscpos_data *data, char *buf, int nr)
{
return sprintf(buf, "1\n");
}
static ssize_t set_temp_reset(struct i2c_client *client, struct fscpos_data
*data, const char *buf, size_t count, int nr, int reg)
{
unsigned long v = simple_strtoul(buf, NULL, 10);
if (v != 1) {
dev_err(&client->dev, "temp_reset value %ld not supported. "
"Use 1 to reset the alarm!\n", v);
return -EINVAL;
}
dev_info(&client->dev, "You used the temp_reset feature which has not "
"been proplerly tested. Please report your "
"experience to the module author.\n");
/* Supported value: 2 (clears the status) */
fscpos_write_value(client, FSCPOS_REG_TEMP_STATE[nr], 2);
return count;
}
/* Fans */
#define RPM_FROM_REG(val) ((val) * 60)
static ssize_t show_fan_status(struct fscpos_data *data, char *buf, int nr)
{
/* bits 0..1, 3..7 reserved => mask with 0x04 */
return sprintf(buf, "%u\n", data->fan_status[nr - 1] & 0x04);
}
static ssize_t show_fan_input(struct fscpos_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[nr - 1]));
}
static ssize_t show_fan_ripple(struct fscpos_data *data, char *buf, int nr)
{
/* bits 2..7 reserved => mask with 0x03 */
return sprintf(buf, "%u\n", data->fan_ripple[nr - 1] & 0x03);
}
static ssize_t set_fan_ripple(struct i2c_client *client, struct fscpos_data
*data, const char *buf, size_t count, int nr, int reg)
{
/* supported values: 2, 4, 8 */
unsigned long v = simple_strtoul(buf, NULL, 10);
switch (v) {
case 2: v = 1; break;
case 4: v = 2; break;
case 8: v = 3; break;
default:
dev_err(&client->dev, "fan_ripple value %ld not supported. "
"Must be one of 2, 4 or 8!\n", v);
return -EINVAL;
}
down(&data->update_lock);
/* bits 2..7 reserved => mask with 0x03 */
data->fan_ripple[nr - 1] &= ~0x03;
data->fan_ripple[nr - 1] |= v;
fscpos_write_value(client, reg, data->fan_ripple[nr - 1]);
up(&data->update_lock);
return count;
}
static ssize_t show_pwm(struct fscpos_data *data, char *buf, int nr)
{
return sprintf(buf, "%u\n", data->pwm[nr - 1]);
}
static ssize_t set_pwm(struct i2c_client *client, struct fscpos_data *data,
const char *buf, size_t count, int nr, int reg)
{
unsigned long v = simple_strtoul(buf, NULL, 10);
/* Range: 0..255 */
if (v < 0) v = 0;
if (v > 255) v = 255;
down(&data->update_lock);
data->pwm[nr - 1] = v;
fscpos_write_value(client, reg, data->pwm[nr - 1]);
up(&data->update_lock);
return count;
}
static void reset_fan_alarm(struct i2c_client *client, int nr)
{
fscpos_write_value(client, FSCPOS_REG_FAN_STATE[nr], 4);
}
/* Volts */
#define VOLT_FROM_REG(val, mult) ((val) * (mult) / 255)
static ssize_t show_volt_12(struct device *dev, struct device_attribute *attr, char *buf)
{
struct fscpos_data *data = fscpos_update_device(dev);
return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[0], 14200));
}
static ssize_t show_volt_5(struct device *dev, struct device_attribute *attr, char *buf)
{
struct fscpos_data *data = fscpos_update_device(dev);
return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[1], 6600));
}
static ssize_t show_volt_batt(struct device *dev, struct device_attribute *attr, char *buf)
{
struct fscpos_data *data = fscpos_update_device(dev);
return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[2], 3300));
}
/* Watchdog */
static ssize_t show_wdog_control(struct fscpos_data *data, char *buf)
{
/* bits 0..3 reserved, bit 6 write only => mask with 0xb0 */
return sprintf(buf, "%u\n", data->wdog_control & 0xb0);
}
static ssize_t set_wdog_control(struct i2c_client *client, struct fscpos_data
*data, const char *buf, size_t count, int reg)
{
/* bits 0..3 reserved => mask with 0xf0 */
unsigned long v = simple_strtoul(buf, NULL, 10) & 0xf0;
down(&data->update_lock);
data->wdog_control &= ~0xf0;
data->wdog_control |= v;
fscpos_write_value(client, reg, data->wdog_control);
up(&data->update_lock);
return count;
}
static ssize_t show_wdog_state(struct fscpos_data *data, char *buf)
{
/* bits 0, 2..7 reserved => mask with 0x02 */
return sprintf(buf, "%u\n", data->wdog_state & 0x02);
}
static ssize_t set_wdog_state(struct i2c_client *client, struct fscpos_data
*data, const char *buf, size_t count, int reg)
{
unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02;
/* Valid values: 2 (clear) */
if (v != 2) {
dev_err(&client->dev, "wdog_state value %ld not supported. "
"Must be 2 to clear the state!\n", v);
return -EINVAL;
}
down(&data->update_lock);
data->wdog_state &= ~v;
fscpos_write_value(client, reg, v);
up(&data->update_lock);
return count;
}
static ssize_t show_wdog_preset(struct fscpos_data *data, char *buf)
{
return sprintf(buf, "%u\n", data->wdog_preset);
}
static ssize_t set_wdog_preset(struct i2c_client *client, struct fscpos_data
*data, const char *buf, size_t count, int reg)
{
unsigned long v = simple_strtoul(buf, NULL, 10) & 0xff;
down(&data->update_lock);
data->wdog_preset = v;
fscpos_write_value(client, reg, data->wdog_preset);
up(&data->update_lock);
return count;
}
/* Event */
static ssize_t show_event(struct device *dev, struct device_attribute *attr, char *buf)
{
/* bits 5..7 reserved => mask with 0x1f */
struct fscpos_data *data = fscpos_update_device(dev);
return sprintf(buf, "%u\n", data->global_event & 0x9b);
}
/*
* Sysfs stuff
*/
#define create_getter(kind, sub) \
static ssize_t sysfs_show_##kind##sub(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct fscpos_data *data = fscpos_update_device(dev); \
return show_##kind##sub(data, buf); \
}
#define create_getter_n(kind, offset, sub) \
static ssize_t sysfs_show_##kind##offset##sub(struct device *dev, struct device_attribute *attr, char\
*buf) \
{ \
struct fscpos_data *data = fscpos_update_device(dev); \
return show_##kind##sub(data, buf, offset); \
}
#define create_setter(kind, sub, reg) \
static ssize_t sysfs_set_##kind##sub (struct device *dev, struct device_attribute *attr, const char \
*buf, size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct fscpos_data *data = i2c_get_clientdata(client); \
return set_##kind##sub(client, data, buf, count, reg); \
}
#define create_setter_n(kind, offset, sub, reg) \
static ssize_t sysfs_set_##kind##offset##sub (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct fscpos_data *data = i2c_get_clientdata(client); \
return set_##kind##sub(client, data, buf, count, offset, reg);\
}
#define create_sysfs_device_ro(kind, sub, offset) \
static DEVICE_ATTR(kind##offset##sub, S_IRUGO, \
sysfs_show_##kind##offset##sub, NULL);
#define create_sysfs_device_rw(kind, sub, offset) \
static DEVICE_ATTR(kind##offset##sub, S_IRUGO | S_IWUSR, \
sysfs_show_##kind##offset##sub, sysfs_set_##kind##offset##sub);
#define sysfs_ro_n(kind, sub, offset) \
create_getter_n(kind, offset, sub); \
create_sysfs_device_ro(kind, sub, offset);
#define sysfs_rw_n(kind, sub, offset, reg) \
create_getter_n(kind, offset, sub); \
create_setter_n(kind, offset, sub, reg); \
create_sysfs_device_rw(kind, sub, offset);
#define sysfs_rw(kind, sub, reg) \
create_getter(kind, sub); \
create_setter(kind, sub, reg); \
create_sysfs_device_rw(kind, sub,);
#define sysfs_fan_with_min(offset, reg_status, reg_ripple, reg_min) \
sysfs_fan(offset, reg_status, reg_ripple); \
sysfs_rw_n(pwm,, offset, reg_min);
#define sysfs_fan(offset, reg_status, reg_ripple) \
sysfs_ro_n(fan, _input, offset); \
sysfs_ro_n(fan, _status, offset); \
sysfs_rw_n(fan, _ripple, offset, reg_ripple);
#define sysfs_temp(offset, reg_status) \
sysfs_ro_n(temp, _input, offset); \
sysfs_ro_n(temp, _status, offset); \
sysfs_rw_n(temp, _reset, offset, reg_status);
#define sysfs_watchdog(reg_wdog_preset, reg_wdog_state, reg_wdog_control) \
sysfs_rw(wdog, _control, reg_wdog_control); \
sysfs_rw(wdog, _preset, reg_wdog_preset); \
sysfs_rw(wdog, _state, reg_wdog_state);
sysfs_fan_with_min(1, FSCPOS_REG_FAN_STATE[0], FSCPOS_REG_FAN_RIPPLE[0],
FSCPOS_REG_PWM[0]);
sysfs_fan_with_min(2, FSCPOS_REG_FAN_STATE[1], FSCPOS_REG_FAN_RIPPLE[1],
FSCPOS_REG_PWM[1]);
sysfs_fan(3, FSCPOS_REG_FAN_STATE[2], FSCPOS_REG_FAN_RIPPLE[2]);
sysfs_temp(1, FSCPOS_REG_TEMP_STATE[0]);
sysfs_temp(2, FSCPOS_REG_TEMP_STATE[1]);
sysfs_temp(3, FSCPOS_REG_TEMP_STATE[2]);
sysfs_watchdog(FSCPOS_REG_WDOG_PRESET, FSCPOS_REG_WDOG_STATE,
FSCPOS_REG_WDOG_CONTROL);
static DEVICE_ATTR(event, S_IRUGO, show_event, NULL);
static DEVICE_ATTR(in0_input, S_IRUGO, show_volt_12, NULL);
static DEVICE_ATTR(in1_input, S_IRUGO, show_volt_5, NULL);
static DEVICE_ATTR(in2_input, S_IRUGO, show_volt_batt, NULL);
static int fscpos_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, fscpos_detect);
}
int fscpos_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct fscpos_data *data;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
/*
* OK. For now, we presume we have a valid client. We now create the
* client structure, even though we cannot fill it completely yet.
* But it allows us to access fscpos_{read,write}_value.
*/
if (!(data = kmalloc(sizeof(struct fscpos_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct fscpos_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &fscpos_driver;
new_client->flags = 0;
/* Do the remaining detection unless force or force_fscpos parameter */
if (kind < 0) {
if ((fscpos_read_value(new_client, FSCPOS_REG_IDENT_0)
!= 0x50) /* 'P' */
|| (fscpos_read_value(new_client, FSCPOS_REG_IDENT_1)
!= 0x45) /* 'E' */
|| (fscpos_read_value(new_client, FSCPOS_REG_IDENT_2)
!= 0x47))/* 'G' */
{
dev_dbg(&new_client->dev, "fscpos detection failed\n");
goto exit_free;
}
}
/* Fill in the remaining client fields and put it in the global list */
strlcpy(new_client->name, "fscpos", I2C_NAME_SIZE);
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 exit_free;
/* Inizialize the fscpos chip */
fscpos_init_client(new_client);
/* Announce that the chip was found */
dev_info(&new_client->dev, "Found fscpos chip, rev %u\n", data->revision);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_event);
device_create_file(&new_client->dev, &dev_attr_in0_input);
device_create_file(&new_client->dev, &dev_attr_in1_input);
device_create_file(&new_client->dev, &dev_attr_in2_input);
device_create_file(&new_client->dev, &dev_attr_wdog_control);
device_create_file(&new_client->dev, &dev_attr_wdog_preset);
device_create_file(&new_client->dev, &dev_attr_wdog_state);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_status);
device_create_file(&new_client->dev, &dev_attr_temp1_reset);
device_create_file(&new_client->dev, &dev_attr_temp2_input);
device_create_file(&new_client->dev, &dev_attr_temp2_status);
device_create_file(&new_client->dev, &dev_attr_temp2_reset);
device_create_file(&new_client->dev, &dev_attr_temp3_input);
device_create_file(&new_client->dev, &dev_attr_temp3_status);
device_create_file(&new_client->dev, &dev_attr_temp3_reset);
device_create_file(&new_client->dev, &dev_attr_fan1_input);
device_create_file(&new_client->dev, &dev_attr_fan1_status);
device_create_file(&new_client->dev, &dev_attr_fan1_ripple);
device_create_file(&new_client->dev, &dev_attr_pwm1);
device_create_file(&new_client->dev, &dev_attr_fan2_input);
device_create_file(&new_client->dev, &dev_attr_fan2_status);
device_create_file(&new_client->dev, &dev_attr_fan2_ripple);
device_create_file(&new_client->dev, &dev_attr_pwm2);
device_create_file(&new_client->dev, &dev_attr_fan3_input);
device_create_file(&new_client->dev, &dev_attr_fan3_status);
device_create_file(&new_client->dev, &dev_attr_fan3_ripple);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static int fscpos_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;
}
kfree(i2c_get_clientdata(client));
return 0;
}
static int fscpos_read_value(struct i2c_client *client, u8 reg)
{
dev_dbg(&client->dev, "Read reg 0x%02x\n", reg);
return i2c_smbus_read_byte_data(client, reg);
}
static int fscpos_write_value(struct i2c_client *client, u8 reg, u8 value)
{
dev_dbg(&client->dev, "Write reg 0x%02x, val 0x%02x\n", reg, value);
return i2c_smbus_write_byte_data(client, reg, value);
}
/* Called when we have found a new FSCPOS chip */
static void fscpos_init_client(struct i2c_client *client)
{
struct fscpos_data *data = i2c_get_clientdata(client);
/* read revision from chip */
data->revision = fscpos_read_value(client, FSCPOS_REG_REVISION);
}
static struct fscpos_data *fscpos_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct fscpos_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if ((jiffies - data->last_updated > 2 * HZ) ||
(jiffies < data->last_updated) || !data->valid) {
int i;
dev_dbg(&client->dev, "Starting fscpos update\n");
for (i = 0; i < 3; i++) {
data->temp_act[i] = fscpos_read_value(client,
FSCPOS_REG_TEMP_ACT[i]);
data->temp_status[i] = fscpos_read_value(client,
FSCPOS_REG_TEMP_STATE[i]);
data->fan_act[i] = fscpos_read_value(client,
FSCPOS_REG_FAN_ACT[i]);
data->fan_status[i] = fscpos_read_value(client,
FSCPOS_REG_FAN_STATE[i]);
data->fan_ripple[i] = fscpos_read_value(client,
FSCPOS_REG_FAN_RIPPLE[i]);
if (i < 2) {
/* fan2_min is not supported by the chip */
data->pwm[i] = fscpos_read_value(client,
FSCPOS_REG_PWM[i]);
}
/* reset fan status if speed is back to > 0 */
if (data->fan_status[i] != 0 && data->fan_act[i] > 0) {
reset_fan_alarm(client, i);
}
}
data->volt[0] = fscpos_read_value(client, FSCPOS_REG_VOLT_12);
data->volt[1] = fscpos_read_value(client, FSCPOS_REG_VOLT_5);
data->volt[2] = fscpos_read_value(client, FSCPOS_REG_VOLT_BATT);
data->wdog_preset = fscpos_read_value(client,
FSCPOS_REG_WDOG_PRESET);
data->wdog_state = fscpos_read_value(client,
FSCPOS_REG_WDOG_STATE);
data->wdog_control = fscpos_read_value(client,
FSCPOS_REG_WDOG_CONTROL);
data->global_event = fscpos_read_value(client,
FSCPOS_REG_EVENT_STATE);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sm_fscpos_init(void)
{
return i2c_add_driver(&fscpos_driver);
}
static void __exit sm_fscpos_exit(void)
{
i2c_del_driver(&fscpos_driver);
}
MODULE_AUTHOR("Stefan Ott <stefan@desire.ch> based on work from Hermann Jung "
"<hej@odn.de>, Frodo Looijaard <frodol@dds.nl>"
" and Philip Edelbrock <phil@netroedge.com>");
MODULE_DESCRIPTION("fujitsu siemens poseidon chip driver");
MODULE_LICENSE("GPL");
module_init(sm_fscpos_init);
module_exit(sm_fscpos_exit);

604
drivers/hwmon/gl518sm.c Normal file
Näytä tiedosto

@@ -0,0 +1,604 @@
/*
* gl518sm.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
* Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
* Kyosti Malkki <kmalkki@cc.hut.fi>
* Copyright (C) 2004 Hong-Gunn Chew <hglinux@gunnet.org> and
* Jean Delvare <khali@linux-fr.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.
*
* Ported to Linux 2.6 by Hong-Gunn Chew with the help of Jean Delvare
* and advice of Greg Kroah-Hartman.
*
* Notes about the port:
* Release 0x00 of the GL518SM chipset doesn't support reading of in0,
* in1 nor in2. The original driver had an ugly workaround to get them
* anyway (changing limits and watching alarms trigger and wear off).
* We did not keep that part of the original driver in the Linux 2.6
* version, since it was making the driver significantly more complex
* with no real benefit.
*
* History:
* 2004-01-28 Original port. (Hong-Gunn Chew)
* 2004-01-31 Code review and approval. (Jean Delvare)
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_2(gl518sm_r00, gl518sm_r80);
/* Many GL518 constants specified below */
/* The GL518 registers */
#define GL518_REG_CHIP_ID 0x00
#define GL518_REG_REVISION 0x01
#define GL518_REG_VENDOR_ID 0x02
#define GL518_REG_CONF 0x03
#define GL518_REG_TEMP_IN 0x04
#define GL518_REG_TEMP_MAX 0x05
#define GL518_REG_TEMP_HYST 0x06
#define GL518_REG_FAN_COUNT 0x07
#define GL518_REG_FAN_LIMIT 0x08
#define GL518_REG_VIN1_LIMIT 0x09
#define GL518_REG_VIN2_LIMIT 0x0a
#define GL518_REG_VIN3_LIMIT 0x0b
#define GL518_REG_VDD_LIMIT 0x0c
#define GL518_REG_VIN3 0x0d
#define GL518_REG_MISC 0x0f
#define GL518_REG_ALARM 0x10
#define GL518_REG_MASK 0x11
#define GL518_REG_INT 0x12
#define GL518_REG_VIN2 0x13
#define GL518_REG_VIN1 0x14
#define GL518_REG_VDD 0x15
/*
* 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 RAW_FROM_REG(val) val
#define BOOL_FROM_REG(val) ((val)?0:1)
#define BOOL_TO_REG(val) ((val)?0:1)
#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0? \
(val)-500:(val)+500)/1000)+119),0,255))
#define TEMP_FROM_REG(val) (((val) - 119) * 1000)
static inline u8 FAN_TO_REG(long rpm, int div)
{
long rpmdiv;
if (rpm == 0)
return 0;
rpmdiv = SENSORS_LIMIT(rpm, 1, 1920000) * div;
return SENSORS_LIMIT((960000 + rpmdiv / 2) / rpmdiv, 1, 255);
}
#define FAN_FROM_REG(val,div) ((val)==0 ? 0 : (960000/((val)*(div))))
#define IN_TO_REG(val) (SENSORS_LIMIT((((val)+9)/19),0,255))
#define IN_FROM_REG(val) ((val)*19)
#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*4+47)/95),0,255))
#define VDD_FROM_REG(val) (((val)*95+2)/4)
#define DIV_TO_REG(val) ((val)==4?2:(val)==2?1:(val)==1?0:3)
#define DIV_FROM_REG(val) (1 << (val))
#define BEEP_MASK_TO_REG(val) ((val) & 0x7f & data->alarm_mask)
#define BEEP_MASK_FROM_REG(val) ((val) & 0x7f)
/* Each client has this additional data */
struct gl518_data {
struct i2c_client client;
enum chips type;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u8 voltage_in[4]; /* Register values; [0] = VDD */
u8 voltage_min[4]; /* Register values; [0] = VDD */
u8 voltage_max[4]; /* Register values; [0] = VDD */
u8 iter_voltage_in[4]; /* Register values; [0] = VDD */
u8 fan_in[2];
u8 fan_min[2];
u8 fan_div[2]; /* Register encoding, shifted right */
u8 fan_auto1; /* Boolean */
u8 temp_in; /* Register values */
u8 temp_max; /* Register values */
u8 temp_hyst; /* Register values */
u8 alarms; /* Register value */
u8 alarm_mask; /* Register value */
u8 beep_mask; /* Register value */
u8 beep_enable; /* Boolean */
};
static int gl518_attach_adapter(struct i2c_adapter *adapter);
static int gl518_detect(struct i2c_adapter *adapter, int address, int kind);
static void gl518_init_client(struct i2c_client *client);
static int gl518_detach_client(struct i2c_client *client);
static int gl518_read_value(struct i2c_client *client, u8 reg);
static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value);
static struct gl518_data *gl518_update_device(struct device *dev);
/* This is the driver that will be inserted */
static struct i2c_driver gl518_driver = {
.owner = THIS_MODULE,
.name = "gl518sm",
.id = I2C_DRIVERID_GL518,
.flags = I2C_DF_NOTIFY,
.attach_adapter = gl518_attach_adapter,
.detach_client = gl518_detach_client,
};
/*
* Sysfs stuff
*/
#define show(type, suffix, value) \
static ssize_t show_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct gl518_data *data = gl518_update_device(dev); \
return sprintf(buf, "%d\n", type##_FROM_REG(data->value)); \
}
#define show_fan(suffix, value, index) \
static ssize_t show_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct gl518_data *data = gl518_update_device(dev); \
return sprintf(buf, "%d\n", FAN_FROM_REG(data->value[index], \
DIV_FROM_REG(data->fan_div[index]))); \
}
show(TEMP, temp_input1, temp_in);
show(TEMP, temp_max1, temp_max);
show(TEMP, temp_hyst1, temp_hyst);
show(BOOL, fan_auto1, fan_auto1);
show_fan(fan_input1, fan_in, 0);
show_fan(fan_input2, fan_in, 1);
show_fan(fan_min1, fan_min, 0);
show_fan(fan_min2, fan_min, 1);
show(DIV, fan_div1, fan_div[0]);
show(DIV, fan_div2, fan_div[1]);
show(VDD, in_input0, voltage_in[0]);
show(IN, in_input1, voltage_in[1]);
show(IN, in_input2, voltage_in[2]);
show(IN, in_input3, voltage_in[3]);
show(VDD, in_min0, voltage_min[0]);
show(IN, in_min1, voltage_min[1]);
show(IN, in_min2, voltage_min[2]);
show(IN, in_min3, voltage_min[3]);
show(VDD, in_max0, voltage_max[0]);
show(IN, in_max1, voltage_max[1]);
show(IN, in_max2, voltage_max[2]);
show(IN, in_max3, voltage_max[3]);
show(RAW, alarms, alarms);
show(BOOL, beep_enable, beep_enable);
show(BEEP_MASK, beep_mask, beep_mask);
#define set(type, suffix, value, reg) \
static ssize_t set_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct gl518_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock); \
data->value = type##_TO_REG(val); \
gl518_write_value(client, reg, data->value); \
up(&data->update_lock); \
return count; \
}
#define set_bits(type, suffix, value, reg, mask, shift) \
static ssize_t set_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct gl518_data *data = i2c_get_clientdata(client); \
int regvalue; \
unsigned long val = simple_strtoul(buf, NULL, 10); \
\
down(&data->update_lock); \
regvalue = gl518_read_value(client, reg); \
data->value = type##_TO_REG(val); \
regvalue = (regvalue & ~mask) | (data->value << shift); \
gl518_write_value(client, reg, regvalue); \
up(&data->update_lock); \
return count; \
}
#define set_low(type, suffix, value, reg) \
set_bits(type, suffix, value, reg, 0x00ff, 0)
#define set_high(type, suffix, value, reg) \
set_bits(type, suffix, value, reg, 0xff00, 8)
set(TEMP, temp_max1, temp_max, GL518_REG_TEMP_MAX);
set(TEMP, temp_hyst1, temp_hyst, GL518_REG_TEMP_HYST);
set_bits(BOOL, fan_auto1, fan_auto1, GL518_REG_MISC, 0x08, 3);
set_bits(DIV, fan_div1, fan_div[0], GL518_REG_MISC, 0xc0, 6);
set_bits(DIV, fan_div2, fan_div[1], GL518_REG_MISC, 0x30, 4);
set_low(VDD, in_min0, voltage_min[0], GL518_REG_VDD_LIMIT);
set_low(IN, in_min1, voltage_min[1], GL518_REG_VIN1_LIMIT);
set_low(IN, in_min2, voltage_min[2], GL518_REG_VIN2_LIMIT);
set_low(IN, in_min3, voltage_min[3], GL518_REG_VIN3_LIMIT);
set_high(VDD, in_max0, voltage_max[0], GL518_REG_VDD_LIMIT);
set_high(IN, in_max1, voltage_max[1], GL518_REG_VIN1_LIMIT);
set_high(IN, in_max2, voltage_max[2], GL518_REG_VIN2_LIMIT);
set_high(IN, in_max3, voltage_max[3], GL518_REG_VIN3_LIMIT);
set_bits(BOOL, beep_enable, beep_enable, GL518_REG_CONF, 0x04, 2);
set(BEEP_MASK, beep_mask, beep_mask, GL518_REG_ALARM);
static ssize_t set_fan_min1(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct gl518_data *data = i2c_get_clientdata(client);
int regvalue;
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
regvalue = gl518_read_value(client, GL518_REG_FAN_LIMIT);
data->fan_min[0] = FAN_TO_REG(val,
DIV_FROM_REG(data->fan_div[0]));
regvalue = (regvalue & 0x00ff) | (data->fan_min[0] << 8);
gl518_write_value(client, GL518_REG_FAN_LIMIT, regvalue);
data->beep_mask = gl518_read_value(client, GL518_REG_ALARM);
if (data->fan_min[0] == 0)
data->alarm_mask &= ~0x20;
else
data->alarm_mask |= 0x20;
data->beep_mask &= data->alarm_mask;
gl518_write_value(client, GL518_REG_ALARM, data->beep_mask);
up(&data->update_lock);
return count;
}
static ssize_t set_fan_min2(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct gl518_data *data = i2c_get_clientdata(client);
int regvalue;
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
regvalue = gl518_read_value(client, GL518_REG_FAN_LIMIT);
data->fan_min[1] = FAN_TO_REG(val,
DIV_FROM_REG(data->fan_div[1]));
regvalue = (regvalue & 0xff00) | data->fan_min[1];
gl518_write_value(client, GL518_REG_FAN_LIMIT, regvalue);
data->beep_mask = gl518_read_value(client, GL518_REG_ALARM);
if (data->fan_min[1] == 0)
data->alarm_mask &= ~0x40;
else
data->alarm_mask |= 0x40;
data->beep_mask &= data->alarm_mask;
gl518_write_value(client, GL518_REG_ALARM, data->beep_mask);
up(&data->update_lock);
return count;
}
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL);
static DEVICE_ATTR(temp1_max, S_IWUSR|S_IRUGO, show_temp_max1, set_temp_max1);
static DEVICE_ATTR(temp1_max_hyst, S_IWUSR|S_IRUGO,
show_temp_hyst1, set_temp_hyst1);
static DEVICE_ATTR(fan1_auto, S_IWUSR|S_IRUGO, show_fan_auto1, set_fan_auto1);
static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input1, NULL);
static DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input2, NULL);
static DEVICE_ATTR(fan1_min, S_IWUSR|S_IRUGO, show_fan_min1, set_fan_min1);
static DEVICE_ATTR(fan2_min, S_IWUSR|S_IRUGO, show_fan_min2, set_fan_min2);
static DEVICE_ATTR(fan1_div, S_IWUSR|S_IRUGO, show_fan_div1, set_fan_div1);
static DEVICE_ATTR(fan2_div, S_IWUSR|S_IRUGO, show_fan_div2, set_fan_div2);
static DEVICE_ATTR(in0_input, S_IRUGO, show_in_input0, NULL);
static DEVICE_ATTR(in1_input, S_IRUGO, show_in_input1, NULL);
static DEVICE_ATTR(in2_input, S_IRUGO, show_in_input2, NULL);
static DEVICE_ATTR(in3_input, S_IRUGO, show_in_input3, NULL);
static DEVICE_ATTR(in0_min, S_IWUSR|S_IRUGO, show_in_min0, set_in_min0);
static DEVICE_ATTR(in1_min, S_IWUSR|S_IRUGO, show_in_min1, set_in_min1);
static DEVICE_ATTR(in2_min, S_IWUSR|S_IRUGO, show_in_min2, set_in_min2);
static DEVICE_ATTR(in3_min, S_IWUSR|S_IRUGO, show_in_min3, set_in_min3);
static DEVICE_ATTR(in0_max, S_IWUSR|S_IRUGO, show_in_max0, set_in_max0);
static DEVICE_ATTR(in1_max, S_IWUSR|S_IRUGO, show_in_max1, set_in_max1);
static DEVICE_ATTR(in2_max, S_IWUSR|S_IRUGO, show_in_max2, set_in_max2);
static DEVICE_ATTR(in3_max, S_IWUSR|S_IRUGO, show_in_max3, set_in_max3);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
static DEVICE_ATTR(beep_enable, S_IWUSR|S_IRUGO,
show_beep_enable, set_beep_enable);
static DEVICE_ATTR(beep_mask, S_IWUSR|S_IRUGO,
show_beep_mask, set_beep_mask);
/*
* Real code
*/
static int gl518_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, gl518_detect);
}
static int gl518_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i;
struct i2c_client *new_client;
struct gl518_data *data;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
But it allows us to access gl518_{read,write}_value. */
if (!(data = kmalloc(sizeof(struct gl518_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct gl518_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &gl518_driver;
new_client->flags = 0;
/* Now, we do the remaining detection. */
if (kind < 0) {
if ((gl518_read_value(new_client, GL518_REG_CHIP_ID) != 0x80)
|| (gl518_read_value(new_client, GL518_REG_CONF) & 0x80))
goto exit_free;
}
/* Determine the chip type. */
if (kind <= 0) {
i = gl518_read_value(new_client, GL518_REG_REVISION);
if (i == 0x00) {
kind = gl518sm_r00;
} else if (i == 0x80) {
kind = gl518sm_r80;
} else {
if (kind <= 0)
dev_info(&adapter->dev,
"Ignoring 'force' parameter for unknown "
"chip at adapter %d, address 0x%02x\n",
i2c_adapter_id(adapter), address);
goto exit_free;
}
}
/* Fill in the remaining client fields */
strlcpy(new_client->name, "gl518sm", I2C_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 exit_free;
/* Initialize the GL518SM chip */
data->alarm_mask = 0xff;
data->voltage_in[0]=data->voltage_in[1]=data->voltage_in[2]=0;
gl518_init_client((struct i2c_client *) new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_in0_input);
device_create_file(&new_client->dev, &dev_attr_in1_input);
device_create_file(&new_client->dev, &dev_attr_in2_input);
device_create_file(&new_client->dev, &dev_attr_in3_input);
device_create_file(&new_client->dev, &dev_attr_in0_min);
device_create_file(&new_client->dev, &dev_attr_in1_min);
device_create_file(&new_client->dev, &dev_attr_in2_min);
device_create_file(&new_client->dev, &dev_attr_in3_min);
device_create_file(&new_client->dev, &dev_attr_in0_max);
device_create_file(&new_client->dev, &dev_attr_in1_max);
device_create_file(&new_client->dev, &dev_attr_in2_max);
device_create_file(&new_client->dev, &dev_attr_in3_max);
device_create_file(&new_client->dev, &dev_attr_fan1_auto);
device_create_file(&new_client->dev, &dev_attr_fan1_input);
device_create_file(&new_client->dev, &dev_attr_fan2_input);
device_create_file(&new_client->dev, &dev_attr_fan1_min);
device_create_file(&new_client->dev, &dev_attr_fan2_min);
device_create_file(&new_client->dev, &dev_attr_fan1_div);
device_create_file(&new_client->dev, &dev_attr_fan2_div);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
device_create_file(&new_client->dev, &dev_attr_alarms);
device_create_file(&new_client->dev, &dev_attr_beep_enable);
device_create_file(&new_client->dev, &dev_attr_beep_mask);
return 0;
/* OK, this is not exactly good programming practice, usually. But it is
very code-efficient in this case. */
exit_free:
kfree(data);
exit:
return err;
}
/* Called when we have found a new GL518SM.
Note that we preserve D4:NoFan2 and D2:beep_enable. */
static void gl518_init_client(struct i2c_client *client)
{
/* Make sure we leave D7:Reset untouched */
u8 regvalue = gl518_read_value(client, GL518_REG_CONF) & 0x7f;
/* Comparator mode (D3=0), standby mode (D6=0) */
gl518_write_value(client, GL518_REG_CONF, (regvalue &= 0x37));
/* Never interrupts */
gl518_write_value(client, GL518_REG_MASK, 0x00);
/* Clear status register (D5=1), start (D6=1) */
gl518_write_value(client, GL518_REG_CONF, 0x20 | regvalue);
gl518_write_value(client, GL518_REG_CONF, 0x40 | regvalue);
}
static int gl518_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;
}
kfree(i2c_get_clientdata(client));
return 0;
}
/* Registers 0x07 to 0x0c are word-sized, others are byte-sized
GL518 uses a high-byte first convention, which is exactly opposite to
the usual practice. */
static int gl518_read_value(struct i2c_client *client, u8 reg)
{
if ((reg >= 0x07) && (reg <= 0x0c))
return swab16(i2c_smbus_read_word_data(client, reg));
else
return i2c_smbus_read_byte_data(client, reg);
}
/* Registers 0x07 to 0x0c are word-sized, others are byte-sized
GL518 uses a high-byte first convention, which is exactly opposite to
the usual practice. */
static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if ((reg >= 0x07) && (reg <= 0x0c))
return i2c_smbus_write_word_data(client, reg, swab16(value));
else
return i2c_smbus_write_byte_data(client, reg, value);
}
static struct gl518_data *gl518_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct gl518_data *data = i2c_get_clientdata(client);
int val;
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
dev_dbg(&client->dev, "Starting gl518 update\n");
data->alarms = gl518_read_value(client, GL518_REG_INT);
data->beep_mask = gl518_read_value(client, GL518_REG_ALARM);
val = gl518_read_value(client, GL518_REG_VDD_LIMIT);
data->voltage_min[0] = val & 0xff;
data->voltage_max[0] = (val >> 8) & 0xff;
val = gl518_read_value(client, GL518_REG_VIN1_LIMIT);
data->voltage_min[1] = val & 0xff;
data->voltage_max[1] = (val >> 8) & 0xff;
val = gl518_read_value(client, GL518_REG_VIN2_LIMIT);
data->voltage_min[2] = val & 0xff;
data->voltage_max[2] = (val >> 8) & 0xff;
val = gl518_read_value(client, GL518_REG_VIN3_LIMIT);
data->voltage_min[3] = val & 0xff;
data->voltage_max[3] = (val >> 8) & 0xff;
val = gl518_read_value(client, GL518_REG_FAN_COUNT);
data->fan_in[0] = (val >> 8) & 0xff;
data->fan_in[1] = val & 0xff;
val = gl518_read_value(client, GL518_REG_FAN_LIMIT);
data->fan_min[0] = (val >> 8) & 0xff;
data->fan_min[1] = val & 0xff;
data->temp_in = gl518_read_value(client, GL518_REG_TEMP_IN);
data->temp_max =
gl518_read_value(client, GL518_REG_TEMP_MAX);
data->temp_hyst =
gl518_read_value(client, GL518_REG_TEMP_HYST);
val = gl518_read_value(client, GL518_REG_MISC);
data->fan_div[0] = (val >> 6) & 0x03;
data->fan_div[1] = (val >> 4) & 0x03;
data->fan_auto1 = (val >> 3) & 0x01;
data->alarms &= data->alarm_mask;
val = gl518_read_value(client, GL518_REG_CONF);
data->beep_enable = (val >> 2) & 1;
if (data->type != gl518sm_r00) {
data->voltage_in[0] =
gl518_read_value(client, GL518_REG_VDD);
data->voltage_in[1] =
gl518_read_value(client, GL518_REG_VIN1);
data->voltage_in[2] =
gl518_read_value(client, GL518_REG_VIN2);
}
data->voltage_in[3] =
gl518_read_value(client, GL518_REG_VIN3);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_gl518sm_init(void)
{
return i2c_add_driver(&gl518_driver);
}
static void __exit sensors_gl518sm_exit(void)
{
i2c_del_driver(&gl518_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
"Kyosti Malkki <kmalkki@cc.hut.fi> and "
"Hong-Gunn Chew <hglinux@gunnet.org>");
MODULE_DESCRIPTION("GL518SM driver");
MODULE_LICENSE("GPL");
module_init(sensors_gl518sm_init);
module_exit(sensors_gl518sm_exit);

769
drivers/hwmon/gl520sm.c Normal file
Näytä tiedosto

@@ -0,0 +1,769 @@
/*
gl520sm.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>,
Ky<4B>sti M<>lkki <kmalkki@cc.hut.fi>
Copyright (c) 2005 Maarten Deprez <maartendeprez@users.sourceforge.net>
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.
*/
#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>
/* Type of the extra sensor */
static unsigned short extra_sensor_type;
module_param(extra_sensor_type, ushort, 0);
MODULE_PARM_DESC(extra_sensor_type, "Type of extra sensor (0=autodetect, 1=temperature, 2=voltage)");
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(gl520sm);
/* Many GL520 constants specified below
One of the inputs can be configured as either temp or voltage.
That's why _TEMP2 and _IN4 access the same register
*/
/* The GL520 registers */
#define GL520_REG_CHIP_ID 0x00
#define GL520_REG_REVISION 0x01
#define GL520_REG_CONF 0x03
#define GL520_REG_MASK 0x11
#define GL520_REG_VID_INPUT 0x02
#define GL520_REG_IN0_INPUT 0x15
#define GL520_REG_IN0_LIMIT 0x0c
#define GL520_REG_IN0_MIN GL520_REG_IN0_LIMIT
#define GL520_REG_IN0_MAX GL520_REG_IN0_LIMIT
#define GL520_REG_IN1_INPUT 0x14
#define GL520_REG_IN1_LIMIT 0x09
#define GL520_REG_IN1_MIN GL520_REG_IN1_LIMIT
#define GL520_REG_IN1_MAX GL520_REG_IN1_LIMIT
#define GL520_REG_IN2_INPUT 0x13
#define GL520_REG_IN2_LIMIT 0x0a
#define GL520_REG_IN2_MIN GL520_REG_IN2_LIMIT
#define GL520_REG_IN2_MAX GL520_REG_IN2_LIMIT
#define GL520_REG_IN3_INPUT 0x0d
#define GL520_REG_IN3_LIMIT 0x0b
#define GL520_REG_IN3_MIN GL520_REG_IN3_LIMIT
#define GL520_REG_IN3_MAX GL520_REG_IN3_LIMIT
#define GL520_REG_IN4_INPUT 0x0e
#define GL520_REG_IN4_MAX 0x17
#define GL520_REG_IN4_MIN 0x18
#define GL520_REG_TEMP1_INPUT 0x04
#define GL520_REG_TEMP1_MAX 0x05
#define GL520_REG_TEMP1_MAX_HYST 0x06
#define GL520_REG_TEMP2_INPUT 0x0e
#define GL520_REG_TEMP2_MAX 0x17
#define GL520_REG_TEMP2_MAX_HYST 0x18
#define GL520_REG_FAN_INPUT 0x07
#define GL520_REG_FAN_MIN 0x08
#define GL520_REG_FAN_DIV 0x0f
#define GL520_REG_FAN_OFF GL520_REG_FAN_DIV
#define GL520_REG_ALARMS 0x12
#define GL520_REG_BEEP_MASK 0x10
#define GL520_REG_BEEP_ENABLE GL520_REG_CONF
/*
* Function declarations
*/
static int gl520_attach_adapter(struct i2c_adapter *adapter);
static int gl520_detect(struct i2c_adapter *adapter, int address, int kind);
static void gl520_init_client(struct i2c_client *client);
static int gl520_detach_client(struct i2c_client *client);
static int gl520_read_value(struct i2c_client *client, u8 reg);
static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value);
static struct gl520_data *gl520_update_device(struct device *dev);
/* Driver data */
static struct i2c_driver gl520_driver = {
.owner = THIS_MODULE,
.name = "gl520sm",
.id = I2C_DRIVERID_GL520,
.flags = I2C_DF_NOTIFY,
.attach_adapter = gl520_attach_adapter,
.detach_client = gl520_detach_client,
};
/* Client data */
struct gl520_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* zero until the following fields are valid */
unsigned long last_updated; /* in jiffies */
u8 vid;
u8 vrm;
u8 in_input[5]; /* [0] = VVD */
u8 in_min[5]; /* [0] = VDD */
u8 in_max[5]; /* [0] = VDD */
u8 fan_input[2];
u8 fan_min[2];
u8 fan_div[2];
u8 fan_off;
u8 temp_input[2];
u8 temp_max[2];
u8 temp_max_hyst[2];
u8 alarms;
u8 beep_enable;
u8 beep_mask;
u8 alarm_mask;
u8 two_temps;
};
/*
* Sysfs stuff
*/
#define sysfs_r(type, n, item, reg) \
static ssize_t get_##type##item (struct gl520_data *, char *, int); \
static ssize_t get_##type##n##item (struct device *, struct device_attribute *attr, char *); \
static ssize_t get_##type##n##item (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct gl520_data *data = gl520_update_device(dev); \
return get_##type##item(data, buf, (n)); \
}
#define sysfs_w(type, n, item, reg) \
static ssize_t set_##type##item (struct i2c_client *, struct gl520_data *, const char *, size_t, int, int); \
static ssize_t set_##type##n##item (struct device *, struct device_attribute *attr, const char *, size_t); \
static ssize_t set_##type##n##item (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct gl520_data *data = i2c_get_clientdata(client); \
return set_##type##item(client, data, buf, count, (n), reg); \
}
#define sysfs_rw_n(type, n, item, reg) \
sysfs_r(type, n, item, reg) \
sysfs_w(type, n, item, reg) \
static DEVICE_ATTR(type##n##item, S_IRUGO | S_IWUSR, get_##type##n##item, set_##type##n##item);
#define sysfs_ro_n(type, n, item, reg) \
sysfs_r(type, n, item, reg) \
static DEVICE_ATTR(type##n##item, S_IRUGO, get_##type##n##item, NULL);
#define sysfs_rw(type, item, reg) \
sysfs_r(type, 0, item, reg) \
sysfs_w(type, 0, item, reg) \
static DEVICE_ATTR(type##item, S_IRUGO | S_IWUSR, get_##type##0##item, set_##type##0##item);
#define sysfs_ro(type, item, reg) \
sysfs_r(type, 0, item, reg) \
static DEVICE_ATTR(type##item, S_IRUGO, get_##type##0##item, NULL);
#define sysfs_vid(n) \
sysfs_ro_n(cpu, n, _vid, GL520_REG_VID_INPUT)
#define device_create_file_vid(client, n) \
device_create_file(&client->dev, &dev_attr_cpu##n##_vid)
#define sysfs_in(n) \
sysfs_ro_n(in, n, _input, GL520_REG_IN##n##INPUT) \
sysfs_rw_n(in, n, _min, GL520_REG_IN##n##_MIN) \
sysfs_rw_n(in, n, _max, GL520_REG_IN##n##_MAX) \
#define device_create_file_in(client, n) \
({device_create_file(&client->dev, &dev_attr_in##n##_input); \
device_create_file(&client->dev, &dev_attr_in##n##_min); \
device_create_file(&client->dev, &dev_attr_in##n##_max);})
#define sysfs_fan(n) \
sysfs_ro_n(fan, n, _input, GL520_REG_FAN_INPUT) \
sysfs_rw_n(fan, n, _min, GL520_REG_FAN_MIN) \
sysfs_rw_n(fan, n, _div, GL520_REG_FAN_DIV)
#define device_create_file_fan(client, n) \
({device_create_file(&client->dev, &dev_attr_fan##n##_input); \
device_create_file(&client->dev, &dev_attr_fan##n##_min); \
device_create_file(&client->dev, &dev_attr_fan##n##_div);})
#define sysfs_fan_off(n) \
sysfs_rw_n(fan, n, _off, GL520_REG_FAN_OFF) \
#define device_create_file_fan_off(client, n) \
device_create_file(&client->dev, &dev_attr_fan##n##_off)
#define sysfs_temp(n) \
sysfs_ro_n(temp, n, _input, GL520_REG_TEMP##n##_INPUT) \
sysfs_rw_n(temp, n, _max, GL520_REG_TEMP##n##_MAX) \
sysfs_rw_n(temp, n, _max_hyst, GL520_REG_TEMP##n##_MAX_HYST)
#define device_create_file_temp(client, n) \
({device_create_file(&client->dev, &dev_attr_temp##n##_input); \
device_create_file(&client->dev, &dev_attr_temp##n##_max); \
device_create_file(&client->dev, &dev_attr_temp##n##_max_hyst);})
#define sysfs_alarms() \
sysfs_ro(alarms, , GL520_REG_ALARMS) \
sysfs_rw(beep_enable, , GL520_REG_BEEP_ENABLE) \
sysfs_rw(beep_mask, , GL520_REG_BEEP_MASK)
#define device_create_file_alarms(client) \
({device_create_file(&client->dev, &dev_attr_alarms); \
device_create_file(&client->dev, &dev_attr_beep_enable); \
device_create_file(&client->dev, &dev_attr_beep_mask);})
sysfs_vid(0)
sysfs_in(0)
sysfs_in(1)
sysfs_in(2)
sysfs_in(3)
sysfs_in(4)
sysfs_fan(1)
sysfs_fan(2)
sysfs_fan_off(1)
sysfs_temp(1)
sysfs_temp(2)
sysfs_alarms()
static ssize_t get_cpu_vid(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));
}
#define VDD_FROM_REG(val) (((val)*95+2)/4)
#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*4+47)/95),0,255))
#define IN_FROM_REG(val) ((val)*19)
#define IN_TO_REG(val) (SENSORS_LIMIT((((val)+9)/19),0,255))
static ssize_t get_in_input(struct gl520_data *data, char *buf, int n)
{
u8 r = data->in_input[n];
if (n == 0)
return sprintf(buf, "%d\n", VDD_FROM_REG(r));
else
return sprintf(buf, "%d\n", IN_FROM_REG(r));
}
static ssize_t get_in_min(struct gl520_data *data, char *buf, int n)
{
u8 r = data->in_min[n];
if (n == 0)
return sprintf(buf, "%d\n", VDD_FROM_REG(r));
else
return sprintf(buf, "%d\n", IN_FROM_REG(r));
}
static ssize_t get_in_max(struct gl520_data *data, char *buf, int n)
{
u8 r = data->in_max[n];
if (n == 0)
return sprintf(buf, "%d\n", VDD_FROM_REG(r));
else
return sprintf(buf, "%d\n", IN_FROM_REG(r));
}
static ssize_t set_in_min(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
{
long v = simple_strtol(buf, NULL, 10);
u8 r;
down(&data->update_lock);
if (n == 0)
r = VDD_TO_REG(v);
else
r = IN_TO_REG(v);
data->in_min[n] = r;
if (n < 4)
gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff) | r);
else
gl520_write_value(client, reg, r);
up(&data->update_lock);
return count;
}
static ssize_t set_in_max(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
{
long v = simple_strtol(buf, NULL, 10);
u8 r;
if (n == 0)
r = VDD_TO_REG(v);
else
r = IN_TO_REG(v);
down(&data->update_lock);
data->in_max[n] = r;
if (n < 4)
gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff00) | (r << 8));
else
gl520_write_value(client, reg, r);
up(&data->update_lock);
return count;
}
#define DIV_FROM_REG(val) (1 << (val))
#define FAN_FROM_REG(val,div) ((val)==0 ? 0 : (480000/((val) << (div))))
#define FAN_TO_REG(val,div) ((val)<=0?0:SENSORS_LIMIT((480000 + ((val) << ((div)-1))) / ((val) << (div)), 1, 255));
static ssize_t get_fan_input(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_input[n - 1], data->fan_div[n - 1]));
}
static ssize_t get_fan_min(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[n - 1], data->fan_div[n - 1]));
}
static ssize_t get_fan_div(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[n - 1]));
}
static ssize_t get_fan_off(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", data->fan_off);
}
static ssize_t set_fan_min(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
{
unsigned long v = simple_strtoul(buf, NULL, 10);
u8 r;
down(&data->update_lock);
r = FAN_TO_REG(v, data->fan_div[n - 1]);
data->fan_min[n - 1] = r;
if (n == 1)
gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff00) | (r << 8));
else
gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff) | r);
data->beep_mask = gl520_read_value(client, GL520_REG_BEEP_MASK);
if (data->fan_min[n - 1] == 0)
data->alarm_mask &= (n == 1) ? ~0x20 : ~0x40;
else
data->alarm_mask |= (n == 1) ? 0x20 : 0x40;
data->beep_mask &= data->alarm_mask;
gl520_write_value(client, GL520_REG_BEEP_MASK, data->beep_mask);
up(&data->update_lock);
return count;
}
static ssize_t set_fan_div(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
{
unsigned long v = simple_strtoul(buf, NULL, 10);
u8 r;
switch (v) {
case 1: r = 0; break;
case 2: r = 1; break;
case 4: r = 2; break;
case 8: r = 3; break;
default:
dev_err(&client->dev, "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", v);
return -EINVAL;
}
down(&data->update_lock);
data->fan_div[n - 1] = r;
if (n == 1)
gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xc0) | (r << 6));
else
gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0x30) | (r << 4));
up(&data->update_lock);
return count;
}
static ssize_t set_fan_off(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
{
u8 r = simple_strtoul(buf, NULL, 10)?1:0;
down(&data->update_lock);
data->fan_off = r;
gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0x0c) | (r << 2));
up(&data->update_lock);
return count;
}
#define TEMP_FROM_REG(val) (((val) - 130) * 1000)
#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0?(val)-500:(val)+500) / 1000)+130),0,255))
static ssize_t get_temp_input(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_input[n - 1]));
}
static ssize_t get_temp_max(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[n - 1]));
}
static ssize_t get_temp_max_hyst(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max_hyst[n - 1]));
}
static ssize_t set_temp_max(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
{
long v = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_max[n - 1] = TEMP_TO_REG(v);;
gl520_write_value(client, reg, data->temp_max[n - 1]);
up(&data->update_lock);
return count;
}
static ssize_t set_temp_max_hyst(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
{
long v = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_max_hyst[n - 1] = TEMP_TO_REG(v);
gl520_write_value(client, reg, data->temp_max_hyst[n - 1]);
up(&data->update_lock);
return count;
}
static ssize_t get_alarms(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", data->alarms);
}
static ssize_t get_beep_enable(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", data->beep_enable);
}
static ssize_t get_beep_mask(struct gl520_data *data, char *buf, int n)
{
return sprintf(buf, "%d\n", data->beep_mask);
}
static ssize_t set_beep_enable(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
{
u8 r = simple_strtoul(buf, NULL, 10)?0:1;
down(&data->update_lock);
data->beep_enable = !r;
gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0x04) | (r << 2));
up(&data->update_lock);
return count;
}
static ssize_t set_beep_mask(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
{
u8 r = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
r &= data->alarm_mask;
data->beep_mask = r;
gl520_write_value(client, reg, r);
up(&data->update_lock);
return count;
}
/*
* Real code
*/
static int gl520_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, gl520_detect);
}
static int gl520_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct gl520_data *data;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
But it allows us to access gl520_{read,write}_value. */
if (!(data = kmalloc(sizeof(struct gl520_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct gl520_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &gl520_driver;
new_client->flags = 0;
/* Determine the chip type. */
if (kind < 0) {
if ((gl520_read_value(new_client, GL520_REG_CHIP_ID) != 0x20) ||
((gl520_read_value(new_client, GL520_REG_REVISION) & 0x7f) != 0x00) ||
((gl520_read_value(new_client, GL520_REG_CONF) & 0x80) != 0x00)) {
dev_dbg(&new_client->dev, "Unknown chip type, skipping\n");
goto exit_free;
}
}
/* Fill in the remaining client fields */
strlcpy(new_client->name, "gl520sm", I2C_NAME_SIZE);
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 exit_free;
/* Initialize the GL520SM chip */
gl520_init_client(new_client);
/* Register sysfs hooks */
device_create_file_vid(new_client, 0);
device_create_file_in(new_client, 0);
device_create_file_in(new_client, 1);
device_create_file_in(new_client, 2);
device_create_file_in(new_client, 3);
if (!data->two_temps)
device_create_file_in(new_client, 4);
device_create_file_fan(new_client, 1);
device_create_file_fan(new_client, 2);
device_create_file_fan_off(new_client, 1);
device_create_file_temp(new_client, 1);
if (data->two_temps)
device_create_file_temp(new_client, 2);
device_create_file_alarms(new_client);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
/* Called when we have found a new GL520SM. */
static void gl520_init_client(struct i2c_client *client)
{
struct gl520_data *data = i2c_get_clientdata(client);
u8 oldconf, conf;
conf = oldconf = gl520_read_value(client, GL520_REG_CONF);
data->alarm_mask = 0xff;
data->vrm = i2c_which_vrm();
if (extra_sensor_type == 1)
conf &= ~0x10;
else if (extra_sensor_type == 2)
conf |= 0x10;
data->two_temps = !(conf & 0x10);
/* If IRQ# is disabled, we can safely force comparator mode */
if (!(conf & 0x20))
conf &= 0xf7;
/* Enable monitoring if needed */
conf |= 0x40;
if (conf != oldconf)
gl520_write_value(client, GL520_REG_CONF, conf);
gl520_update_device(&(client->dev));
if (data->fan_min[0] == 0)
data->alarm_mask &= ~0x20;
if (data->fan_min[1] == 0)
data->alarm_mask &= ~0x40;
data->beep_mask &= data->alarm_mask;
gl520_write_value(client, GL520_REG_BEEP_MASK, data->beep_mask);
}
static int gl520_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;
}
kfree(i2c_get_clientdata(client));
return 0;
}
/* Registers 0x07 to 0x0c are word-sized, others are byte-sized
GL520 uses a high-byte first convention */
static int gl520_read_value(struct i2c_client *client, u8 reg)
{
if ((reg >= 0x07) && (reg <= 0x0c))
return swab16(i2c_smbus_read_word_data(client, reg));
else
return i2c_smbus_read_byte_data(client, reg);
}
static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if ((reg >= 0x07) && (reg <= 0x0c))
return i2c_smbus_write_word_data(client, reg, swab16(value));
else
return i2c_smbus_write_byte_data(client, reg, value);
}
static struct gl520_data *gl520_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct gl520_data *data = i2c_get_clientdata(client);
int val;
down(&data->update_lock);
if ((jiffies - data->last_updated > 2 * HZ) ||
(jiffies < data->last_updated) || !data->valid) {
dev_dbg(&client->dev, "Starting gl520sm update\n");
data->alarms = gl520_read_value(client, GL520_REG_ALARMS);
data->beep_mask = gl520_read_value(client, GL520_REG_BEEP_MASK);
data->vid = gl520_read_value(client, GL520_REG_VID_INPUT) & 0x1f;
val = gl520_read_value(client, GL520_REG_IN0_LIMIT);
data->in_min[0] = val & 0xff;
data->in_max[0] = (val >> 8) & 0xff;
val = gl520_read_value(client, GL520_REG_IN1_LIMIT);
data->in_min[1] = val & 0xff;
data->in_max[1] = (val >> 8) & 0xff;
val = gl520_read_value(client, GL520_REG_IN2_LIMIT);
data->in_min[2] = val & 0xff;
data->in_max[2] = (val >> 8) & 0xff;
val = gl520_read_value(client, GL520_REG_IN3_LIMIT);
data->in_min[3] = val & 0xff;
data->in_max[3] = (val >> 8) & 0xff;
val = gl520_read_value(client, GL520_REG_FAN_INPUT);
data->fan_input[0] = (val >> 8) & 0xff;
data->fan_input[1] = val & 0xff;
val = gl520_read_value(client, GL520_REG_FAN_MIN);
data->fan_min[0] = (val >> 8) & 0xff;
data->fan_min[1] = val & 0xff;
data->temp_input[0] = gl520_read_value(client, GL520_REG_TEMP1_INPUT);
data->temp_max[0] = gl520_read_value(client, GL520_REG_TEMP1_MAX);
data->temp_max_hyst[0] = gl520_read_value(client, GL520_REG_TEMP1_MAX_HYST);
val = gl520_read_value(client, GL520_REG_FAN_DIV);
data->fan_div[0] = (val >> 6) & 0x03;
data->fan_div[1] = (val >> 4) & 0x03;
data->fan_off = (val >> 2) & 0x01;
data->alarms &= data->alarm_mask;
val = gl520_read_value(client, GL520_REG_CONF);
data->beep_enable = !((val >> 2) & 1);
data->in_input[0] = gl520_read_value(client, GL520_REG_IN0_INPUT);
data->in_input[1] = gl520_read_value(client, GL520_REG_IN1_INPUT);
data->in_input[2] = gl520_read_value(client, GL520_REG_IN2_INPUT);
data->in_input[3] = gl520_read_value(client, GL520_REG_IN3_INPUT);
/* Temp1 and Vin4 are the same input */
if (data->two_temps) {
data->temp_input[1] = gl520_read_value(client, GL520_REG_TEMP2_INPUT);
data->temp_max[1] = gl520_read_value(client, GL520_REG_TEMP2_MAX);
data->temp_max_hyst[1] = gl520_read_value(client, GL520_REG_TEMP2_MAX_HYST);
} else {
data->in_input[4] = gl520_read_value(client, GL520_REG_IN4_INPUT);
data->in_min[4] = gl520_read_value(client, GL520_REG_IN4_MIN);
data->in_max[4] = gl520_read_value(client, GL520_REG_IN4_MAX);
}
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_gl520sm_init(void)
{
return i2c_add_driver(&gl520_driver);
}
static void __exit sensors_gl520sm_exit(void)
{
i2c_del_driver(&gl520_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
"Ky<EFBFBD>sti M<>lkki <kmalkki@cc.hut.fi>, "
"Maarten Deprez <maartendeprez@users.sourceforge.net>");
MODULE_DESCRIPTION("GL520SM driver");
MODULE_LICENSE("GPL");
module_init(sensors_gl520sm_init);
module_exit(sensors_gl520sm_exit);

1184
drivers/hwmon/it87.c Normal file

File diff suppressed because it is too large Load Diff

597
drivers/hwmon/lm63.c Normal file
Näytä tiedosto

@@ -0,0 +1,597 @@
/*
* lm63.c - driver for the National Semiconductor LM63 temperature sensor
* with integrated fan control
* Copyright (C) 2004-2005 Jean Delvare <khali@linux-fr.org>
* Based on the lm90 driver.
*
* The LM63 is a sensor chip made by National Semiconductor. It measures
* two temperatures (its own and one external one) and the speed of one
* fan, those speed it can additionally control. Complete datasheet can be
* obtained from National's website at:
* http://www.national.com/pf/LM/LM63.html
*
* The LM63 is basically an LM86 with fan speed monitoring and control
* capabilities added. It misses some of the LM86 features though:
* - No low limit for local temperature.
* - No critical limit for local temperature.
* - Critical limit for remote temperature can be changed only once. We
* will consider that the critical limit is read-only.
*
* The datasheet isn't very clear about what the tachometer reading is.
* I had a explanation from National Semiconductor though. The two lower
* bits of the read value have to be masked out. The value is still 16 bit
* in width.
*
* 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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/hwmon-sysfs.h>
/*
* Addresses to scan
* Address is fully defined internally and cannot be changed.
*/
static unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_1(lm63);
/*
* The LM63 registers
*/
#define LM63_REG_CONFIG1 0x03
#define LM63_REG_CONFIG2 0xBF
#define LM63_REG_CONFIG_FAN 0x4A
#define LM63_REG_TACH_COUNT_MSB 0x47
#define LM63_REG_TACH_COUNT_LSB 0x46
#define LM63_REG_TACH_LIMIT_MSB 0x49
#define LM63_REG_TACH_LIMIT_LSB 0x48
#define LM63_REG_PWM_VALUE 0x4C
#define LM63_REG_PWM_FREQ 0x4D
#define LM63_REG_LOCAL_TEMP 0x00
#define LM63_REG_LOCAL_HIGH 0x05
#define LM63_REG_REMOTE_TEMP_MSB 0x01
#define LM63_REG_REMOTE_TEMP_LSB 0x10
#define LM63_REG_REMOTE_OFFSET_MSB 0x11
#define LM63_REG_REMOTE_OFFSET_LSB 0x12
#define LM63_REG_REMOTE_HIGH_MSB 0x07
#define LM63_REG_REMOTE_HIGH_LSB 0x13
#define LM63_REG_REMOTE_LOW_MSB 0x08
#define LM63_REG_REMOTE_LOW_LSB 0x14
#define LM63_REG_REMOTE_TCRIT 0x19
#define LM63_REG_REMOTE_TCRIT_HYST 0x21
#define LM63_REG_ALERT_STATUS 0x02
#define LM63_REG_ALERT_MASK 0x16
#define LM63_REG_MAN_ID 0xFE
#define LM63_REG_CHIP_ID 0xFF
/*
* Conversions and various macros
* For tachometer counts, the LM63 uses 16-bit values.
* For local temperature and high limit, remote critical limit and hysteresis
* value, it uses signed 8-bit values with LSB = 1 degree Celsius.
* For remote temperature, low and high limits, it uses signed 11-bit values
* with LSB = 0.125 degree Celsius, left-justified in 16-bit registers.
*/
#define FAN_FROM_REG(reg) ((reg) == 0xFFFC || (reg) == 0 ? 0 : \
5400000 / (reg))
#define FAN_TO_REG(val) ((val) <= 82 ? 0xFFFC : \
(5400000 / (val)) & 0xFFFC)
#define TEMP8_FROM_REG(reg) ((reg) * 1000)
#define TEMP8_TO_REG(val) ((val) <= -128000 ? -128 : \
(val) >= 127000 ? 127 : \
(val) < 0 ? ((val) - 500) / 1000 : \
((val) + 500) / 1000)
#define TEMP11_FROM_REG(reg) ((reg) / 32 * 125)
#define TEMP11_TO_REG(val) ((val) <= -128000 ? 0x8000 : \
(val) >= 127875 ? 0x7FE0 : \
(val) < 0 ? ((val) - 62) / 125 * 32 : \
((val) + 62) / 125 * 32)
#define HYST_TO_REG(val) ((val) <= 0 ? 0 : \
(val) >= 127000 ? 127 : \
((val) + 500) / 1000)
/*
* Functions declaration
*/
static int lm63_attach_adapter(struct i2c_adapter *adapter);
static int lm63_detach_client(struct i2c_client *client);
static struct lm63_data *lm63_update_device(struct device *dev);
static int lm63_detect(struct i2c_adapter *adapter, int address, int kind);
static void lm63_init_client(struct i2c_client *client);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver lm63_driver = {
.owner = THIS_MODULE,
.name = "lm63",
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm63_attach_adapter,
.detach_client = lm63_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct lm63_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
/* registers values */
u8 config, config_fan;
u16 fan[2]; /* 0: input
1: low limit */
u8 pwm1_freq;
u8 pwm1_value;
s8 temp8[3]; /* 0: local input
1: local high limit
2: remote critical limit */
s16 temp11[3]; /* 0: remote input
1: remote low limit
2: remote high limit */
u8 temp2_crit_hyst;
u8 alarms;
};
/*
* Sysfs callback functions and files
*/
static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm63_data *data = lm63_update_device(dev);
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[attr->index]));
}
static ssize_t set_fan(struct device *dev, struct device_attribute *dummy,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm63_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->fan[1] = FAN_TO_REG(val);
i2c_smbus_write_byte_data(client, LM63_REG_TACH_LIMIT_LSB,
data->fan[1] & 0xFF);
i2c_smbus_write_byte_data(client, LM63_REG_TACH_LIMIT_MSB,
data->fan[1] >> 8);
up(&data->update_lock);
return count;
}
static ssize_t show_pwm1(struct device *dev, struct device_attribute *dummy,
char *buf)
{
struct lm63_data *data = lm63_update_device(dev);
return sprintf(buf, "%d\n", data->pwm1_value >= 2 * data->pwm1_freq ?
255 : (data->pwm1_value * 255 + data->pwm1_freq) /
(2 * data->pwm1_freq));
}
static ssize_t set_pwm1(struct device *dev, struct device_attribute *dummy,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm63_data *data = i2c_get_clientdata(client);
unsigned long val;
if (!(data->config_fan & 0x20)) /* register is read-only */
return -EPERM;
val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->pwm1_value = val <= 0 ? 0 :
val >= 255 ? 2 * data->pwm1_freq :
(val * data->pwm1_freq * 2 + 127) / 255;
i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1_value);
up(&data->update_lock);
return count;
}
static ssize_t show_pwm1_enable(struct device *dev, struct device_attribute *dummy,
char *buf)
{
struct lm63_data *data = lm63_update_device(dev);
return sprintf(buf, "%d\n", data->config_fan & 0x20 ? 1 : 2);
}
static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm63_data *data = lm63_update_device(dev);
return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->temp8[attr->index]));
}
static ssize_t set_temp8(struct device *dev, struct device_attribute *dummy,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm63_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp8[1] = TEMP8_TO_REG(val);
i2c_smbus_write_byte_data(client, LM63_REG_LOCAL_HIGH, data->temp8[1]);
up(&data->update_lock);
return count;
}
static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm63_data *data = lm63_update_device(dev);
return sprintf(buf, "%d\n", TEMP11_FROM_REG(data->temp11[attr->index]));
}
static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
static const u8 reg[4] = {
LM63_REG_REMOTE_LOW_MSB,
LM63_REG_REMOTE_LOW_LSB,
LM63_REG_REMOTE_HIGH_MSB,
LM63_REG_REMOTE_HIGH_LSB,
};
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct i2c_client *client = to_i2c_client(dev);
struct lm63_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
int nr = attr->index;
down(&data->update_lock);
data->temp11[nr] = TEMP11_TO_REG(val);
i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2],
data->temp11[nr] >> 8);
i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1],
data->temp11[nr] & 0xff);
up(&data->update_lock);
return count;
}
/* Hysteresis register holds a relative value, while we want to present
an absolute to user-space */
static ssize_t show_temp2_crit_hyst(struct device *dev, struct device_attribute *dummy,
char *buf)
{
struct lm63_data *data = lm63_update_device(dev);
return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->temp8[2])
- TEMP8_FROM_REG(data->temp2_crit_hyst));
}
/* And now the other way around, user-space provides an absolute
hysteresis value and we have to store a relative one */
static ssize_t set_temp2_crit_hyst(struct device *dev, struct device_attribute *dummy,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm63_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
long hyst;
down(&data->update_lock);
hyst = TEMP8_FROM_REG(data->temp8[2]) - val;
i2c_smbus_write_byte_data(client, LM63_REG_REMOTE_TCRIT_HYST,
HYST_TO_REG(hyst));
up(&data->update_lock);
return count;
}
static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy,
char *buf)
{
struct lm63_data *data = lm63_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan,
set_fan, 1);
static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1);
static DEVICE_ATTR(pwm1_enable, S_IRUGO, show_pwm1_enable, NULL);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp8, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8,
set_temp8, 1);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11,
set_temp11, 1);
static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11,
set_temp11, 2);
static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp8, NULL, 2);
static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp2_crit_hyst,
set_temp2_crit_hyst);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
/*
* Real code
*/
static int lm63_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, lm63_detect);
}
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
*/
static int lm63_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct lm63_data *data;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct lm63_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct lm63_data));
/* The common I2C client data is placed right before the
LM63-specific data. */
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm63_driver;
new_client->flags = 0;
/* Default to an LM63 if forced */
if (kind == 0)
kind = lm63;
if (kind < 0) { /* must identify */
u8 man_id, chip_id, reg_config1, reg_config2;
u8 reg_alert_status, reg_alert_mask;
man_id = i2c_smbus_read_byte_data(new_client,
LM63_REG_MAN_ID);
chip_id = i2c_smbus_read_byte_data(new_client,
LM63_REG_CHIP_ID);
reg_config1 = i2c_smbus_read_byte_data(new_client,
LM63_REG_CONFIG1);
reg_config2 = i2c_smbus_read_byte_data(new_client,
LM63_REG_CONFIG2);
reg_alert_status = i2c_smbus_read_byte_data(new_client,
LM63_REG_ALERT_STATUS);
reg_alert_mask = i2c_smbus_read_byte_data(new_client,
LM63_REG_ALERT_MASK);
if (man_id == 0x01 /* National Semiconductor */
&& chip_id == 0x41 /* LM63 */
&& (reg_config1 & 0x18) == 0x00
&& (reg_config2 & 0xF8) == 0x00
&& (reg_alert_status & 0x20) == 0x00
&& (reg_alert_mask & 0xA4) == 0xA4) {
kind = lm63;
} else { /* failed */
dev_dbg(&adapter->dev, "Unsupported chip "
"(man_id=0x%02X, chip_id=0x%02X).\n",
man_id, chip_id);
goto exit_free;
}
}
strlcpy(new_client->name, "lm63", I2C_NAME_SIZE);
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 exit_free;
/* Initialize the LM63 chip */
lm63_init_client(new_client);
/* Register sysfs hooks */
if (data->config & 0x04) { /* tachometer enabled */
device_create_file(&new_client->dev,
&sensor_dev_attr_fan1_input.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_fan1_min.dev_attr);
}
device_create_file(&new_client->dev, &dev_attr_pwm1);
device_create_file(&new_client->dev, &dev_attr_pwm1_enable);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp1_input.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp2_input.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp2_min.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp1_max.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp2_max.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp2_crit.dev_attr);
device_create_file(&new_client->dev, &dev_attr_temp2_crit_hyst);
device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
/* Idealy we shouldn't have to initialize anything, since the BIOS
should have taken care of everything */
static void lm63_init_client(struct i2c_client *client)
{
struct lm63_data *data = i2c_get_clientdata(client);
data->config = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1);
data->config_fan = i2c_smbus_read_byte_data(client,
LM63_REG_CONFIG_FAN);
/* Start converting if needed */
if (data->config & 0x40) { /* standby */
dev_dbg(&client->dev, "Switching to operational mode");
data->config &= 0xA7;
i2c_smbus_write_byte_data(client, LM63_REG_CONFIG1,
data->config);
}
/* We may need pwm1_freq before ever updating the client data */
data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ);
if (data->pwm1_freq == 0)
data->pwm1_freq = 1;
/* Show some debug info about the LM63 configuration */
dev_dbg(&client->dev, "Alert/tach pin configured for %s\n",
(data->config & 0x04) ? "tachometer input" :
"alert output");
dev_dbg(&client->dev, "PWM clock %s kHz, output frequency %u Hz\n",
(data->config_fan & 0x08) ? "1.4" : "360",
((data->config_fan & 0x08) ? 700 : 180000) / data->pwm1_freq);
dev_dbg(&client->dev, "PWM output active %s, %s mode\n",
(data->config_fan & 0x10) ? "low" : "high",
(data->config_fan & 0x20) ? "manual" : "auto");
}
static int lm63_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;
}
kfree(i2c_get_clientdata(client));
return 0;
}
static struct lm63_data *lm63_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm63_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
if (data->config & 0x04) { /* tachometer enabled */
/* order matters for fan1_input */
data->fan[0] = i2c_smbus_read_byte_data(client,
LM63_REG_TACH_COUNT_LSB) & 0xFC;
data->fan[0] |= i2c_smbus_read_byte_data(client,
LM63_REG_TACH_COUNT_MSB) << 8;
data->fan[1] = (i2c_smbus_read_byte_data(client,
LM63_REG_TACH_LIMIT_LSB) & 0xFC)
| (i2c_smbus_read_byte_data(client,
LM63_REG_TACH_LIMIT_MSB) << 8);
}
data->pwm1_freq = i2c_smbus_read_byte_data(client,
LM63_REG_PWM_FREQ);
if (data->pwm1_freq == 0)
data->pwm1_freq = 1;
data->pwm1_value = i2c_smbus_read_byte_data(client,
LM63_REG_PWM_VALUE);
data->temp8[0] = i2c_smbus_read_byte_data(client,
LM63_REG_LOCAL_TEMP);
data->temp8[1] = i2c_smbus_read_byte_data(client,
LM63_REG_LOCAL_HIGH);
/* order matters for temp2_input */
data->temp11[0] = i2c_smbus_read_byte_data(client,
LM63_REG_REMOTE_TEMP_MSB) << 8;
data->temp11[0] |= i2c_smbus_read_byte_data(client,
LM63_REG_REMOTE_TEMP_LSB);
data->temp11[1] = (i2c_smbus_read_byte_data(client,
LM63_REG_REMOTE_LOW_MSB) << 8)
| i2c_smbus_read_byte_data(client,
LM63_REG_REMOTE_LOW_LSB);
data->temp11[2] = (i2c_smbus_read_byte_data(client,
LM63_REG_REMOTE_HIGH_MSB) << 8)
| i2c_smbus_read_byte_data(client,
LM63_REG_REMOTE_HIGH_LSB);
data->temp8[2] = i2c_smbus_read_byte_data(client,
LM63_REG_REMOTE_TCRIT);
data->temp2_crit_hyst = i2c_smbus_read_byte_data(client,
LM63_REG_REMOTE_TCRIT_HYST);
data->alarms = i2c_smbus_read_byte_data(client,
LM63_REG_ALERT_STATUS) & 0x7F;
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_lm63_init(void)
{
return i2c_add_driver(&lm63_driver);
}
static void __exit sensors_lm63_exit(void)
{
i2c_del_driver(&lm63_driver);
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("LM63 driver");
MODULE_LICENSE("GPL");
module_init(sensors_lm63_init);
module_exit(sensors_lm63_exit);

296
drivers/hwmon/lm75.c Normal file
Näytä tiedosto

@@ -0,0 +1,296 @@
/*
lm75.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include "lm75.h"
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(lm75);
/* Many LM75 constants specified below */
/* The LM75 registers */
#define LM75_REG_TEMP 0x00
#define LM75_REG_CONF 0x01
#define LM75_REG_TEMP_HYST 0x02
#define LM75_REG_TEMP_OS 0x03
/* Each client has this additional data */
struct lm75_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u16 temp_input; /* Register values */
u16 temp_max;
u16 temp_hyst;
};
static int lm75_attach_adapter(struct i2c_adapter *adapter);
static int lm75_detect(struct i2c_adapter *adapter, int address, int kind);
static void lm75_init_client(struct i2c_client *client);
static int lm75_detach_client(struct i2c_client *client);
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 struct lm75_data *lm75_update_device(struct device *dev);
/* This is the driver that will be inserted */
static struct i2c_driver lm75_driver = {
.owner = THIS_MODULE,
.name = "lm75",
.id = I2C_DRIVERID_LM75,
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm75_attach_adapter,
.detach_client = lm75_detach_client,
};
#define show(value) \
static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct lm75_data *data = lm75_update_device(dev); \
return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->value)); \
}
show(temp_max);
show(temp_hyst);
show(temp_input);
#define set(value, reg) \
static ssize_t set_##value(struct device *dev, struct device_attribute *attr, 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); \
\
down(&data->update_lock); \
data->value = LM75_TEMP_TO_REG(temp); \
lm75_write_value(client, reg, data->value); \
up(&data->update_lock); \
return count; \
}
set(temp_max, LM75_REG_TEMP_OS);
set(temp_hyst, LM75_REG_TEMP_HYST);
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max);
static DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst);
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL);
static int lm75_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, lm75_detect);
}
/* This function is called by i2c_detect */
static int lm75_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i;
struct i2c_client *new_client;
struct lm75_data *data;
int err = 0;
const char *name = "";
/* Make sure we aren't probing the ISA bus!! This is just a safety check
at this moment; i2c_detect really won't call us. */
#ifdef DEBUG
if (i2c_is_isa_adapter(adapter)) {
dev_dbg(&adapter->dev,
"lm75_detect called for an ISA bus adapter?!?\n");
goto exit;
}
#endif
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
But it allows us to access lm75_{read,write}_value. */
if (!(data = kmalloc(sizeof(struct lm75_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct lm75_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm75_driver;
new_client->flags = 0;
/* Now, we do the remaining detection. There is no identification-
dedicated register so we have to rely on several tricks:
unused bits, registers cycling over 8-address boundaries,
addresses 0x04-0x07 returning the last read value.
The cycling+unused addresses combination is not tested,
since it would significantly slow the detection down and would
hardly add any value. */
if (kind < 0) {
int cur, conf, hyst, os;
/* Unused addresses */
cur = i2c_smbus_read_word_data(new_client, 0);
conf = i2c_smbus_read_byte_data(new_client, 1);
hyst = i2c_smbus_read_word_data(new_client, 2);
if (i2c_smbus_read_word_data(new_client, 4) != hyst
|| i2c_smbus_read_word_data(new_client, 5) != hyst
|| i2c_smbus_read_word_data(new_client, 6) != hyst
|| i2c_smbus_read_word_data(new_client, 7) != hyst)
goto exit_free;
os = i2c_smbus_read_word_data(new_client, 3);
if (i2c_smbus_read_word_data(new_client, 4) != os
|| i2c_smbus_read_word_data(new_client, 5) != os
|| i2c_smbus_read_word_data(new_client, 6) != os
|| i2c_smbus_read_word_data(new_client, 7) != os)
goto exit_free;
/* Unused bits */
if (conf & 0xe0)
goto exit_free;
/* Addresses cycling */
for (i = 8; i < 0xff; i += 8)
if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
|| i2c_smbus_read_word_data(new_client, i + 2) != hyst
|| i2c_smbus_read_word_data(new_client, i + 3) != os)
goto exit_free;
}
/* Determine the chip type - only one kind supported! */
if (kind <= 0)
kind = lm75;
if (kind == lm75) {
name = "lm75";
}
/* Fill in the remaining client fields and put it into the global list */
strlcpy(new_client->name, name, I2C_NAME_SIZE);
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 exit_free;
/* Initialize the LM75 chip */
lm75_init_client(new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static int lm75_detach_client(struct i2c_client *client)
{
i2c_detach_client(client);
kfree(i2c_get_clientdata(client));
return 0;
}
/* All registers are word-sized, except for the configuration register.
LM75 uses a high-byte first convention, which is exactly opposite to
the usual practice. */
static int lm75_read_value(struct i2c_client *client, u8 reg)
{
if (reg == LM75_REG_CONF)
return i2c_smbus_read_byte_data(client, reg);
else
return swab16(i2c_smbus_read_word_data(client, reg));
}
/* All registers are word-sized, except for the configuration register.
LM75 uses a high-byte first convention, which is exactly opposite to
the usual practice. */
static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if (reg == LM75_REG_CONF)
return i2c_smbus_write_byte_data(client, reg, value);
else
return i2c_smbus_write_word_data(client, reg, swab16(value));
}
static void lm75_init_client(struct i2c_client *client)
{
/* Initialize the LM75 chip */
lm75_write_value(client, LM75_REG_CONF, 0);
}
static struct lm75_data *lm75_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm75_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
dev_dbg(&client->dev, "Starting lm75 update\n");
data->temp_input = lm75_read_value(client, LM75_REG_TEMP);
data->temp_max = lm75_read_value(client, LM75_REG_TEMP_OS);
data->temp_hyst = lm75_read_value(client, LM75_REG_TEMP_HYST);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_lm75_init(void)
{
return i2c_add_driver(&lm75_driver);
}
static void __exit sensors_lm75_exit(void)
{
i2c_del_driver(&lm75_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
MODULE_DESCRIPTION("LM75 driver");
MODULE_LICENSE("GPL");
module_init(sensors_lm75_init);
module_exit(sensors_lm75_exit);

49
drivers/hwmon/lm75.h Normal file
Näytä tiedosto

@@ -0,0 +1,49 @@
/*
lm75.h - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.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 file contains common code for encoding/decoding LM75 type
temperature readings, which are emulated by many of the chips
we support. As the user is unlikely to load more than one driver
which contains this code, we don't worry about the wasted space.
*/
#include <linux/i2c-sensor.h>
/* straight from the datasheet */
#define LM75_TEMP_MIN (-55000)
#define LM75_TEMP_MAX 125000
/* TEMP: 0.001C/bit (-55C to +125C)
REG: (0.5C/bit, two's complement) << 7 */
static inline u16 LM75_TEMP_TO_REG(int temp)
{
int ntemp = SENSORS_LIMIT(temp, LM75_TEMP_MIN, LM75_TEMP_MAX);
ntemp += (ntemp<0 ? -250 : 250);
return (u16)((ntemp / 500) << 7);
}
static inline int LM75_TEMP_FROM_REG(u16 reg)
{
/* use integer division instead of equivalent right shift to
guarantee arithmetic shift and preserve the sign */
return ((s16)reg / 128) * 500;
}

420
drivers/hwmon/lm77.c Normal file
Näytä tiedosto

@@ -0,0 +1,420 @@
/*
lm77.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 2004 Andras BALI <drewie@freemail.hu>
Heavily based on lm75.c by Frodo Looijaard <frodol@dds.nl>. The LM77
is a temperature sensor and thermal window comparator with 0.5 deg
resolution made by National Semiconductor. Complete datasheet can be
obtained at their site:
http://www.national.com/pf/LM/LM77.html
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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(lm77);
/* The LM77 registers */
#define LM77_REG_TEMP 0x00
#define LM77_REG_CONF 0x01
#define LM77_REG_TEMP_HYST 0x02
#define LM77_REG_TEMP_CRIT 0x03
#define LM77_REG_TEMP_MIN 0x04
#define LM77_REG_TEMP_MAX 0x05
/* Each client has this additional data */
struct lm77_data {
struct i2c_client client;
struct semaphore update_lock;
char valid;
unsigned long last_updated; /* In jiffies */
int temp_input; /* Temperatures */
int temp_crit;
int temp_min;
int temp_max;
int temp_hyst;
u8 alarms;
};
static int lm77_attach_adapter(struct i2c_adapter *adapter);
static int lm77_detect(struct i2c_adapter *adapter, int address, int kind);
static void lm77_init_client(struct i2c_client *client);
static int lm77_detach_client(struct i2c_client *client);
static u16 lm77_read_value(struct i2c_client *client, u8 reg);
static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value);
static struct lm77_data *lm77_update_device(struct device *dev);
/* This is the driver that will be inserted */
static struct i2c_driver lm77_driver = {
.owner = THIS_MODULE,
.name = "lm77",
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm77_attach_adapter,
.detach_client = lm77_detach_client,
};
/* straight from the datasheet */
#define LM77_TEMP_MIN (-55000)
#define LM77_TEMP_MAX 125000
/* In the temperature registers, the low 3 bits are not part of the
temperature values; they are the status bits. */
static inline u16 LM77_TEMP_TO_REG(int temp)
{
int ntemp = SENSORS_LIMIT(temp, LM77_TEMP_MIN, LM77_TEMP_MAX);
return (u16)((ntemp / 500) * 8);
}
static inline int LM77_TEMP_FROM_REG(u16 reg)
{
return ((int)reg / 8) * 500;
}
/* sysfs stuff */
/* read routines for temperature limits */
#define show(value) \
static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct lm77_data *data = lm77_update_device(dev); \
return sprintf(buf, "%d\n", data->value); \
}
show(temp_input);
show(temp_crit);
show(temp_min);
show(temp_max);
show(alarms);
/* read routines for hysteresis values */
static ssize_t show_temp_crit_hyst(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm77_data *data = lm77_update_device(dev);
return sprintf(buf, "%d\n", data->temp_crit - data->temp_hyst);
}
static ssize_t show_temp_min_hyst(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm77_data *data = lm77_update_device(dev);
return sprintf(buf, "%d\n", data->temp_min + data->temp_hyst);
}
static ssize_t show_temp_max_hyst(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm77_data *data = lm77_update_device(dev);
return sprintf(buf, "%d\n", data->temp_max - data->temp_hyst);
}
/* write routines */
#define set(value, reg) \
static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct lm77_data *data = i2c_get_clientdata(client); \
long val = simple_strtoul(buf, NULL, 10); \
\
down(&data->update_lock); \
data->value = val; \
lm77_write_value(client, reg, LM77_TEMP_TO_REG(data->value)); \
up(&data->update_lock); \
return count; \
}
set(temp_min, LM77_REG_TEMP_MIN);
set(temp_max, LM77_REG_TEMP_MAX);
/* hysteresis is stored as a relative value on the chip, so it has to be
converted first */
static ssize_t set_temp_crit_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm77_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->temp_hyst = data->temp_crit - val;
lm77_write_value(client, LM77_REG_TEMP_HYST,
LM77_TEMP_TO_REG(data->temp_hyst));
up(&data->update_lock);
return count;
}
/* preserve hysteresis when setting T_crit */
static ssize_t set_temp_crit(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm77_data *data = i2c_get_clientdata(client);
long val = simple_strtoul(buf, NULL, 10);
int oldcrithyst;
down(&data->update_lock);
oldcrithyst = data->temp_crit - data->temp_hyst;
data->temp_crit = val;
data->temp_hyst = data->temp_crit - oldcrithyst;
lm77_write_value(client, LM77_REG_TEMP_CRIT,
LM77_TEMP_TO_REG(data->temp_crit));
lm77_write_value(client, LM77_REG_TEMP_HYST,
LM77_TEMP_TO_REG(data->temp_hyst));
up(&data->update_lock);
return count;
}
static DEVICE_ATTR(temp1_input, S_IRUGO,
show_temp_input, NULL);
static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
show_temp_crit, set_temp_crit);
static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
show_temp_min, set_temp_min);
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
show_temp_max, set_temp_max);
static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO,
show_temp_crit_hyst, set_temp_crit_hyst);
static DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
show_temp_min_hyst, NULL);
static DEVICE_ATTR(temp1_max_hyst, S_IRUGO,
show_temp_max_hyst, NULL);
static DEVICE_ATTR(alarms, S_IRUGO,
show_alarms, NULL);
static int lm77_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, lm77_detect);
}
/* This function is called by i2c_detect */
static int lm77_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct lm77_data *data;
int err = 0;
const char *name = "";
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
But it allows us to access lm77_{read,write}_value. */
if (!(data = kmalloc(sizeof(struct lm77_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct lm77_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm77_driver;
new_client->flags = 0;
/* Here comes the remaining detection. Since the LM77 has no
register dedicated to identification, we have to rely on the
following tricks:
1. the high 4 bits represent the sign and thus they should
always be the same
2. the high 3 bits are unused in the configuration register
3. addresses 0x06 and 0x07 return the last read value
4. registers cycling over 8-address boundaries
Word-sized registers are high-byte first. */
if (kind < 0) {
int i, cur, conf, hyst, crit, min, max;
/* addresses cycling */
cur = i2c_smbus_read_word_data(new_client, 0);
conf = i2c_smbus_read_byte_data(new_client, 1);
hyst = i2c_smbus_read_word_data(new_client, 2);
crit = i2c_smbus_read_word_data(new_client, 3);
min = i2c_smbus_read_word_data(new_client, 4);
max = i2c_smbus_read_word_data(new_client, 5);
for (i = 8; i <= 0xff; i += 8)
if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
|| i2c_smbus_read_word_data(new_client, i + 2) != hyst
|| i2c_smbus_read_word_data(new_client, i + 3) != crit
|| i2c_smbus_read_word_data(new_client, i + 4) != min
|| i2c_smbus_read_word_data(new_client, i + 5) != max)
goto exit_free;
/* sign bits */
if (((cur & 0x00f0) != 0xf0 && (cur & 0x00f0) != 0x0)
|| ((hyst & 0x00f0) != 0xf0 && (hyst & 0x00f0) != 0x0)
|| ((crit & 0x00f0) != 0xf0 && (crit & 0x00f0) != 0x0)
|| ((min & 0x00f0) != 0xf0 && (min & 0x00f0) != 0x0)
|| ((max & 0x00f0) != 0xf0 && (max & 0x00f0) != 0x0))
goto exit_free;
/* unused bits */
if (conf & 0xe0)
goto exit_free;
/* 0x06 and 0x07 return the last read value */
cur = i2c_smbus_read_word_data(new_client, 0);
if (i2c_smbus_read_word_data(new_client, 6) != cur
|| i2c_smbus_read_word_data(new_client, 7) != cur)
goto exit_free;
hyst = i2c_smbus_read_word_data(new_client, 2);
if (i2c_smbus_read_word_data(new_client, 6) != hyst
|| i2c_smbus_read_word_data(new_client, 7) != hyst)
goto exit_free;
min = i2c_smbus_read_word_data(new_client, 4);
if (i2c_smbus_read_word_data(new_client, 6) != min
|| i2c_smbus_read_word_data(new_client, 7) != min)
goto exit_free;
}
/* Determine the chip type - only one kind supported! */
if (kind <= 0)
kind = lm77;
if (kind == lm77) {
name = "lm77";
}
/* Fill in the remaining client fields and put it into the global list */
strlcpy(new_client->name, name, I2C_NAME_SIZE);
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 exit_free;
/* Initialize the LM77 chip */
lm77_init_client(new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_crit);
device_create_file(&new_client->dev, &dev_attr_temp1_min);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst);
device_create_file(&new_client->dev, &dev_attr_temp1_min_hyst);
device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static int lm77_detach_client(struct i2c_client *client)
{
i2c_detach_client(client);
kfree(i2c_get_clientdata(client));
return 0;
}
/* All registers are word-sized, except for the configuration register.
The LM77 uses the high-byte first convention. */
static u16 lm77_read_value(struct i2c_client *client, u8 reg)
{
if (reg == LM77_REG_CONF)
return i2c_smbus_read_byte_data(client, reg);
else
return swab16(i2c_smbus_read_word_data(client, reg));
}
static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if (reg == LM77_REG_CONF)
return i2c_smbus_write_byte_data(client, reg, value);
else
return i2c_smbus_write_word_data(client, reg, swab16(value));
}
static void lm77_init_client(struct i2c_client *client)
{
/* Initialize the LM77 chip - turn off shutdown mode */
int conf = lm77_read_value(client, LM77_REG_CONF);
if (conf & 1)
lm77_write_value(client, LM77_REG_CONF, conf & 0xfe);
}
static struct lm77_data *lm77_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm77_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
dev_dbg(&client->dev, "Starting lm77 update\n");
data->temp_input =
LM77_TEMP_FROM_REG(lm77_read_value(client,
LM77_REG_TEMP));
data->temp_hyst =
LM77_TEMP_FROM_REG(lm77_read_value(client,
LM77_REG_TEMP_HYST));
data->temp_crit =
LM77_TEMP_FROM_REG(lm77_read_value(client,
LM77_REG_TEMP_CRIT));
data->temp_min =
LM77_TEMP_FROM_REG(lm77_read_value(client,
LM77_REG_TEMP_MIN));
data->temp_max =
LM77_TEMP_FROM_REG(lm77_read_value(client,
LM77_REG_TEMP_MAX));
data->alarms =
lm77_read_value(client, LM77_REG_TEMP) & 0x0007;
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_lm77_init(void)
{
return i2c_add_driver(&lm77_driver);
}
static void __exit sensors_lm77_exit(void)
{
i2c_del_driver(&lm77_driver);
}
MODULE_AUTHOR("Andras BALI <drewie@freemail.hu>");
MODULE_DESCRIPTION("LM77 driver");
MODULE_LICENSE("GPL");
module_init(sensors_lm77_init);
module_exit(sensors_lm77_exit);

795
drivers/hwmon/lm78.c Normal file
Näytä tiedosto

@@ -0,0 +1,795 @@
/*
lm78.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <asm/io.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x20, 0x21, 0x22, 0x23, 0x24,
0x25, 0x26, 0x27, 0x28, 0x29,
0x2a, 0x2b, 0x2c, 0x2d, 0x2e,
0x2f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { 0x0290, I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_3(lm78, lm78j, lm79);
/* Many LM78 constants specified below */
/* Length of ISA address segment */
#define LM78_EXTENT 8
/* Where are the ISA address/data registers relative to the base address */
#define LM78_ADDR_REG_OFFSET 5
#define LM78_DATA_REG_OFFSET 6
/* The LM78 registers */
#define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2)
#define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2)
#define LM78_REG_IN(nr) (0x20 + (nr))
#define LM78_REG_FAN_MIN(nr) (0x3b + (nr))
#define LM78_REG_FAN(nr) (0x28 + (nr))
#define LM78_REG_TEMP 0x27
#define LM78_REG_TEMP_OVER 0x39
#define LM78_REG_TEMP_HYST 0x3a
#define LM78_REG_ALARM1 0x41
#define LM78_REG_ALARM2 0x42
#define LM78_REG_VID_FANDIV 0x47
#define LM78_REG_CONFIG 0x40
#define LM78_REG_CHIPID 0x49
#define LM78_REG_I2C_ADDR 0x48
/* Conversions. Rounding and limit checking is only done on the TO_REG
variants. */
/* IN: mV, (0V to 4.08V)
REG: 16mV/bit */
static inline u8 IN_TO_REG(unsigned long val)
{
unsigned long nval = SENSORS_LIMIT(val, 0, 4080);
return (nval + 8) / 16;
}
#define IN_FROM_REG(val) ((val) * 16)
static inline u8 FAN_TO_REG(long rpm, int div)
{
if (rpm <= 0)
return 255;
return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
}
static inline int FAN_FROM_REG(u8 val, int div)
{
return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div);
}
/* TEMP: mC (-128C to +127C)
REG: 1C/bit, two's complement */
static inline s8 TEMP_TO_REG(int val)
{
int nval = SENSORS_LIMIT(val, -128000, 127000) ;
return nval<0 ? (nval-500)/1000 : (nval+500)/1000;
}
static inline int TEMP_FROM_REG(s8 val)
{
return val * 1000;
}
/* VID: mV
REG: (see doc/vid) */
static inline int VID_FROM_REG(u8 val)
{
return val==0x1f ? 0 : val>=0x10 ? 5100-val*100 : 2050-val*50;
}
#define DIV_FROM_REG(val) (1 << (val))
/* There are some complications in a module like this. First off, LM78 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
LM78 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 LM78 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 LM78, we need to keep some data in memory. That
data is pointed to by lm78_list[NR]->data. The structure itself is
dynamically allocated, at the same time when a new lm78 client is
allocated. */
struct lm78_data {
struct i2c_client client;
struct semaphore lock;
enum chips type;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u8 in[7]; /* Register value */
u8 in_max[7]; /* Register value */
u8 in_min[7]; /* Register value */
u8 fan[3]; /* Register value */
u8 fan_min[3]; /* Register value */
s8 temp; /* Register value */
s8 temp_over; /* Register value */
s8 temp_hyst; /* Register value */
u8 fan_div[3]; /* Register encoding, shifted right */
u8 vid; /* Register encoding, combined */
u16 alarms; /* Register encoding, combined */
};
static int lm78_attach_adapter(struct i2c_adapter *adapter);
static int lm78_detect(struct i2c_adapter *adapter, int address, int kind);
static int lm78_detach_client(struct i2c_client *client);
static int lm78_read_value(struct i2c_client *client, u8 register);
static int lm78_write_value(struct i2c_client *client, u8 register, u8 value);
static struct lm78_data *lm78_update_device(struct device *dev);
static void lm78_init_client(struct i2c_client *client);
static struct i2c_driver lm78_driver = {
.owner = THIS_MODULE,
.name = "lm78",
.id = I2C_DRIVERID_LM78,
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm78_attach_adapter,
.detach_client = lm78_detach_client,
};
/* 7 Voltages */
static ssize_t show_in(struct device *dev, char *buf, int nr)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr]));
}
static ssize_t show_in_min(struct device *dev, char *buf, int nr)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr]));
}
static ssize_t show_in_max(struct device *dev, char *buf, int nr)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr]));
}
static ssize_t set_in_min(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->in_min[nr] = IN_TO_REG(val);
lm78_write_value(client, LM78_REG_IN_MIN(nr), data->in_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t set_in_max(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->in_max[nr] = IN_TO_REG(val);
lm78_write_value(client, LM78_REG_IN_MAX(nr), data->in_max[nr]);
up(&data->update_lock);
return count;
}
#define show_in_offset(offset) \
static ssize_t \
show_in##offset (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_in(dev, buf, offset); \
} \
static DEVICE_ATTR(in##offset##_input, S_IRUGO, \
show_in##offset, NULL); \
static ssize_t \
show_in##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_in_min(dev, buf, offset); \
} \
static ssize_t \
show_in##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_in_max(dev, buf, offset); \
} \
static ssize_t set_in##offset##_min (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_in_min(dev, buf, count, offset); \
} \
static ssize_t set_in##offset##_max (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_in_max(dev, buf, count, offset); \
} \
static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
show_in##offset##_min, set_in##offset##_min); \
static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
show_in##offset##_max, set_in##offset##_max);
show_in_offset(0);
show_in_offset(1);
show_in_offset(2);
show_in_offset(3);
show_in_offset(4);
show_in_offset(5);
show_in_offset(6);
/* Temperature */
static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp));
}
static ssize_t show_temp_over(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over));
}
static ssize_t set_temp_over(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_over = TEMP_TO_REG(val);
lm78_write_value(client, LM78_REG_TEMP_OVER, data->temp_over);
up(&data->update_lock);
return count;
}
static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst));
}
static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_hyst = TEMP_TO_REG(val);
lm78_write_value(client, LM78_REG_TEMP_HYST, data->temp_hyst);
up(&data->update_lock);
return count;
}
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
show_temp_over, set_temp_over);
static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
show_temp_hyst, set_temp_hyst);
/* 3 Fans */
static ssize_t show_fan(struct device *dev, char *buf, int nr)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
DIV_FROM_REG(data->fan_div[nr])) );
}
static ssize_t show_fan_min(struct device *dev, char *buf, int nr)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr],
DIV_FROM_REG(data->fan_div[nr])) );
}
static ssize_t set_fan_min(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
lm78_write_value(client, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) );
}
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan divisor. This follows the principle of
least suprise; the user doesn't expect the fan minimum to change just
because the divisor changed. */
static ssize_t set_fan_div(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
unsigned long min;
u8 reg;
down(&data->update_lock);
min = FAN_FROM_REG(data->fan_min[nr],
DIV_FROM_REG(data->fan_div[nr]));
switch (val) {
case 1: data->fan_div[nr] = 0; break;
case 2: data->fan_div[nr] = 1; break;
case 4: data->fan_div[nr] = 2; break;
case 8: data->fan_div[nr] = 3; break;
default:
dev_err(&client->dev, "fan_div value %ld not "
"supported. Choose one of 1, 2, 4 or 8!\n", val);
up(&data->update_lock);
return -EINVAL;
}
reg = lm78_read_value(client, LM78_REG_VID_FANDIV);
switch (nr) {
case 0:
reg = (reg & 0xcf) | (data->fan_div[nr] << 4);
break;
case 1:
reg = (reg & 0x3f) | (data->fan_div[nr] << 6);
break;
}
lm78_write_value(client, LM78_REG_VID_FANDIV, reg);
data->fan_min[nr] =
FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
lm78_write_value(client, LM78_REG_FAN_MIN(nr), data->fan_min[nr]);
up(&data->update_lock);
return count;
}
#define show_fan_offset(offset) \
static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_fan(dev, buf, offset - 1); \
} \
static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_fan_min(dev, buf, offset - 1); \
} \
static ssize_t show_fan_##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_fan_div(dev, buf, offset - 1); \
} \
static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_fan_min(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\
static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
show_fan_##offset##_min, set_fan_##offset##_min);
static ssize_t set_fan_1_div(struct device *dev, struct device_attribute *attr, const char *buf,
size_t count)
{
return set_fan_div(dev, buf, count, 0) ;
}
static ssize_t set_fan_2_div(struct device *dev, struct device_attribute *attr, const char *buf,
size_t count)
{
return set_fan_div(dev, buf, count, 1) ;
}
show_fan_offset(1);
show_fan_offset(2);
show_fan_offset(3);
/* Fan 3 divisor is locked in H/W */
static DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR,
show_fan_1_div, set_fan_1_div);
static DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR,
show_fan_2_div, set_fan_2_div);
static DEVICE_ATTR(fan3_div, S_IRUGO, show_fan_3_div, NULL);
/* VID */
static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%d\n", VID_FROM_REG(data->vid));
}
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
/* Alarms */
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm78_data *data = lm78_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
/* This function is called when:
* lm78_driver is inserted (when this module is loaded), for each
available adapter
* when a new adapter is inserted (and lm78_driver is still present) */
static int lm78_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, lm78_detect);
}
/* This function is called by i2c_detect */
int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i, err;
struct i2c_client *new_client;
struct lm78_data *data;
const char *client_name = "";
int is_isa = i2c_is_isa_adapter(adapter);
if (!is_isa &&
!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
err = -ENODEV;
goto ERROR0;
}
/* Reserve the ISA region */
if (is_isa)
if (!request_region(address, LM78_EXTENT, lm78_driver.name)) {
err = -EBUSY;
goto ERROR0;
}
/* 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) {
err = -ENODEV;
goto ERROR1;
}
if (inb_p(address + 3) != i) {
err = -ENODEV;
goto ERROR1;
}
if (inb_p(address + 7) != i) {
err = -ENODEV;
goto ERROR1;
}
#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);
err = -ENODEV;
goto ERROR1;
}
}
}
/* 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 lm78_{read,write}_value. */
if (!(data = kmalloc(sizeof(struct lm78_data), GFP_KERNEL))) {
err = -ENOMEM;
goto ERROR1;
}
memset(data, 0, sizeof(struct lm78_data));
new_client = &data->client;
if (is_isa)
init_MUTEX(&data->lock);
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm78_driver;
new_client->flags = 0;
/* Now, we do the remaining detection. */
if (kind < 0) {
if (lm78_read_value(new_client, LM78_REG_CONFIG) & 0x80) {
err = -ENODEV;
goto ERROR2;
}
if (!is_isa && (lm78_read_value(
new_client, LM78_REG_I2C_ADDR) != address)) {
err = -ENODEV;
goto ERROR2;
}
}
/* Determine the chip type. */
if (kind <= 0) {
i = lm78_read_value(new_client, LM78_REG_CHIPID);
if (i == 0x00 || i == 0x20)
kind = lm78;
else if (i == 0x40)
kind = lm78j;
else if ((i & 0xfe) == 0xc0)
kind = lm79;
else {
if (kind == 0)
dev_warn(&adapter->dev, "Ignoring 'force' "
"parameter for unknown chip at "
"adapter %d, address 0x%02x\n",
i2c_adapter_id(adapter), address);
err = -ENODEV;
goto ERROR2;
}
}
if (kind == lm78) {
client_name = "lm78";
} else if (kind == lm78j) {
client_name = "lm78-j";
} else if (kind == lm79) {
client_name = "lm79";
}
/* Fill in the remaining client fields and put into the global list */
strlcpy(new_client->name, client_name, I2C_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 ERROR2;
/* Initialize the LM78 chip */
lm78_init_client(new_client);
/* A few vars need to be filled upon startup */
for (i = 0; i < 3; i++) {
data->fan_min[i] = lm78_read_value(new_client,
LM78_REG_FAN_MIN(i));
}
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_in0_input);
device_create_file(&new_client->dev, &dev_attr_in0_min);
device_create_file(&new_client->dev, &dev_attr_in0_max);
device_create_file(&new_client->dev, &dev_attr_in1_input);
device_create_file(&new_client->dev, &dev_attr_in1_min);
device_create_file(&new_client->dev, &dev_attr_in1_max);
device_create_file(&new_client->dev, &dev_attr_in2_input);
device_create_file(&new_client->dev, &dev_attr_in2_min);
device_create_file(&new_client->dev, &dev_attr_in2_max);
device_create_file(&new_client->dev, &dev_attr_in3_input);
device_create_file(&new_client->dev, &dev_attr_in3_min);
device_create_file(&new_client->dev, &dev_attr_in3_max);
device_create_file(&new_client->dev, &dev_attr_in4_input);
device_create_file(&new_client->dev, &dev_attr_in4_min);
device_create_file(&new_client->dev, &dev_attr_in4_max);
device_create_file(&new_client->dev, &dev_attr_in5_input);
device_create_file(&new_client->dev, &dev_attr_in5_min);
device_create_file(&new_client->dev, &dev_attr_in5_max);
device_create_file(&new_client->dev, &dev_attr_in6_input);
device_create_file(&new_client->dev, &dev_attr_in6_min);
device_create_file(&new_client->dev, &dev_attr_in6_max);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
device_create_file(&new_client->dev, &dev_attr_fan1_input);
device_create_file(&new_client->dev, &dev_attr_fan1_min);
device_create_file(&new_client->dev, &dev_attr_fan1_div);
device_create_file(&new_client->dev, &dev_attr_fan2_input);
device_create_file(&new_client->dev, &dev_attr_fan2_min);
device_create_file(&new_client->dev, &dev_attr_fan2_div);
device_create_file(&new_client->dev, &dev_attr_fan3_input);
device_create_file(&new_client->dev, &dev_attr_fan3_min);
device_create_file(&new_client->dev, &dev_attr_fan3_div);
device_create_file(&new_client->dev, &dev_attr_alarms);
device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
return 0;
ERROR2:
kfree(data);
ERROR1:
if (is_isa)
release_region(address, LM78_EXTENT);
ERROR0:
return err;
}
static int lm78_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;
}
if(i2c_is_isa_client(client))
release_region(client->addr, LM78_EXTENT);
kfree(i2c_get_clientdata(client));
return 0;
}
/* The SMBus locks itself, but ISA access must be locked explicitly!
We don't want to lock the whole ISA bus, so we lock each client
separately.
We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
would slow down the LM78 access and should not be necessary. */
static int lm78_read_value(struct i2c_client *client, u8 reg)
{
int res;
if (i2c_is_isa_client(client)) {
struct lm78_data *data = i2c_get_clientdata(client);
down(&data->lock);
outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
res = inb_p(client->addr + LM78_DATA_REG_OFFSET);
up(&data->lock);
return res;
} else
return i2c_smbus_read_byte_data(client, reg);
}
/* The SMBus locks itself, but ISA access muse be locked explicitly!
We don't want to lock the whole ISA bus, so we lock each client
separately.
We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks,
would slow down the LM78 access and should not be necessary.
There are some ugly typecasts here, but the good new is - they should
nowhere else be necessary! */
static int lm78_write_value(struct i2c_client *client, u8 reg, u8 value)
{
if (i2c_is_isa_client(client)) {
struct lm78_data *data = i2c_get_clientdata(client);
down(&data->lock);
outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET);
outb_p(value, client->addr + LM78_DATA_REG_OFFSET);
up(&data->lock);
return 0;
} else
return i2c_smbus_write_byte_data(client, reg, value);
}
/* Called when we have found a new LM78. It should set limits, etc. */
static void lm78_init_client(struct i2c_client *client)
{
u8 config = lm78_read_value(client, LM78_REG_CONFIG);
/* Start monitoring */
if (!(config & 0x01))
lm78_write_value(client, LM78_REG_CONFIG,
(config & 0xf7) | 0x01);
}
static struct lm78_data *lm78_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm78_data *data = i2c_get_clientdata(client);
int i;
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
dev_dbg(&client->dev, "Starting lm78 update\n");
for (i = 0; i <= 6; i++) {
data->in[i] =
lm78_read_value(client, LM78_REG_IN(i));
data->in_min[i] =
lm78_read_value(client, LM78_REG_IN_MIN(i));
data->in_max[i] =
lm78_read_value(client, LM78_REG_IN_MAX(i));
}
for (i = 0; i < 3; i++) {
data->fan[i] =
lm78_read_value(client, LM78_REG_FAN(i));
data->fan_min[i] =
lm78_read_value(client, LM78_REG_FAN_MIN(i));
}
data->temp = lm78_read_value(client, LM78_REG_TEMP);
data->temp_over =
lm78_read_value(client, LM78_REG_TEMP_OVER);
data->temp_hyst =
lm78_read_value(client, LM78_REG_TEMP_HYST);
i = lm78_read_value(client, LM78_REG_VID_FANDIV);
data->vid = i & 0x0f;
if (data->type == lm79)
data->vid |=
(lm78_read_value(client, LM78_REG_CHIPID) &
0x01) << 4;
else
data->vid |= 0x10;
data->fan_div[0] = (i >> 4) & 0x03;
data->fan_div[1] = i >> 6;
data->alarms = lm78_read_value(client, LM78_REG_ALARM1) +
(lm78_read_value(client, LM78_REG_ALARM2) << 8);
data->last_updated = jiffies;
data->valid = 1;
data->fan_div[2] = 1;
}
up(&data->update_lock);
return data;
}
static int __init sm_lm78_init(void)
{
return i2c_add_driver(&lm78_driver);
}
static void __exit sm_lm78_exit(void)
{
i2c_del_driver(&lm78_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
MODULE_DESCRIPTION("LM78, LM78-J and LM79 driver");
MODULE_LICENSE("GPL");
module_init(sm_lm78_init);
module_exit(sm_lm78_exit);

601
drivers/hwmon/lm80.c Normal file
Näytä tiedosto

@@ -0,0 +1,601 @@
/*
* lm80.c - From lm_sensors, Linux kernel modules for hardware
* monitoring
* Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
* and Philip Edelbrock <phil@netroedge.com>
*
* Ported to Linux 2.6 by Tiago Sousa <mirage@kaotik.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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c,
0x2d, 0x2e, 0x2f, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(lm80);
/* Many LM80 constants specified below */
/* The LM80 registers */
#define LM80_REG_IN_MAX(nr) (0x2a + (nr) * 2)
#define LM80_REG_IN_MIN(nr) (0x2b + (nr) * 2)
#define LM80_REG_IN(nr) (0x20 + (nr))
#define LM80_REG_FAN1 0x28
#define LM80_REG_FAN2 0x29
#define LM80_REG_FAN_MIN(nr) (0x3b + (nr))
#define LM80_REG_TEMP 0x27
#define LM80_REG_TEMP_HOT_MAX 0x38
#define LM80_REG_TEMP_HOT_HYST 0x39
#define LM80_REG_TEMP_OS_MAX 0x3a
#define LM80_REG_TEMP_OS_HYST 0x3b
#define LM80_REG_CONFIG 0x00
#define LM80_REG_ALARM1 0x01
#define LM80_REG_ALARM2 0x02
#define LM80_REG_MASK1 0x03
#define LM80_REG_MASK2 0x04
#define LM80_REG_FANDIV 0x05
#define LM80_REG_RES 0x06
/* 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)+5)/10,0,255))
#define IN_FROM_REG(val) ((val)*10)
static inline unsigned char FAN_TO_REG(unsigned rpm, unsigned 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/((div)*(val)))
static inline long TEMP_FROM_REG(u16 temp)
{
long res;
temp >>= 4;
if (temp < 0x0800)
res = 625 * (long) temp;
else
res = ((long) temp - 0x01000) * 625;
return res / 10;
}
#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*1000)
#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT((val)<0?\
((val)-500)/1000:((val)+500)/1000,0,255)
#define DIV_FROM_REG(val) (1 << (val))
/*
* Client data (each client gets its own)
*/
struct lm80_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u8 in[7]; /* Register value */
u8 in_max[7]; /* Register value */
u8 in_min[7]; /* Register value */
u8 fan[2]; /* Register value */
u8 fan_min[2]; /* Register value */
u8 fan_div[2]; /* Register encoding, shifted right */
u16 temp; /* Register values, shifted right */
u8 temp_hot_max; /* Register value */
u8 temp_hot_hyst; /* Register value */
u8 temp_os_max; /* Register value */
u8 temp_os_hyst; /* Register value */
u16 alarms; /* Register encoding, combined */
};
/*
* Functions declaration
*/
static int lm80_attach_adapter(struct i2c_adapter *adapter);
static int lm80_detect(struct i2c_adapter *adapter, int address, int kind);
static void lm80_init_client(struct i2c_client *client);
static int lm80_detach_client(struct i2c_client *client);
static struct lm80_data *lm80_update_device(struct device *dev);
static int lm80_read_value(struct i2c_client *client, u8 reg);
static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver lm80_driver = {
.owner = THIS_MODULE,
.name = "lm80",
.id = I2C_DRIVERID_LM80,
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm80_attach_adapter,
.detach_client = lm80_detach_client,
};
/*
* Sysfs stuff
*/
#define show_in(suffix, value) \
static ssize_t show_in_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct lm80_data *data = lm80_update_device(dev); \
return sprintf(buf, "%d\n", IN_FROM_REG(data->value)); \
}
show_in(min0, in_min[0]);
show_in(min1, in_min[1]);
show_in(min2, in_min[2]);
show_in(min3, in_min[3]);
show_in(min4, in_min[4]);
show_in(min5, in_min[5]);
show_in(min6, in_min[6]);
show_in(max0, in_max[0]);
show_in(max1, in_max[1]);
show_in(max2, in_max[2]);
show_in(max3, in_max[3]);
show_in(max4, in_max[4]);
show_in(max5, in_max[5]);
show_in(max6, in_max[6]);
show_in(input0, in[0]);
show_in(input1, in[1]);
show_in(input2, in[2]);
show_in(input3, in[3]);
show_in(input4, in[4]);
show_in(input5, in[5]);
show_in(input6, in[6]);
#define set_in(suffix, value, reg) \
static ssize_t set_in_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct lm80_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock);\
data->value = IN_TO_REG(val); \
lm80_write_value(client, reg, data->value); \
up(&data->update_lock);\
return count; \
}
set_in(min0, in_min[0], LM80_REG_IN_MIN(0));
set_in(min1, in_min[1], LM80_REG_IN_MIN(1));
set_in(min2, in_min[2], LM80_REG_IN_MIN(2));
set_in(min3, in_min[3], LM80_REG_IN_MIN(3));
set_in(min4, in_min[4], LM80_REG_IN_MIN(4));
set_in(min5, in_min[5], LM80_REG_IN_MIN(5));
set_in(min6, in_min[6], LM80_REG_IN_MIN(6));
set_in(max0, in_max[0], LM80_REG_IN_MAX(0));
set_in(max1, in_max[1], LM80_REG_IN_MAX(1));
set_in(max2, in_max[2], LM80_REG_IN_MAX(2));
set_in(max3, in_max[3], LM80_REG_IN_MAX(3));
set_in(max4, in_max[4], LM80_REG_IN_MAX(4));
set_in(max5, in_max[5], LM80_REG_IN_MAX(5));
set_in(max6, in_max[6], LM80_REG_IN_MAX(6));
#define show_fan(suffix, value, div) \
static ssize_t show_fan_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct lm80_data *data = lm80_update_device(dev); \
return sprintf(buf, "%d\n", FAN_FROM_REG(data->value, \
DIV_FROM_REG(data->div))); \
}
show_fan(min1, fan_min[0], fan_div[0]);
show_fan(min2, fan_min[1], fan_div[1]);
show_fan(input1, fan[0], fan_div[0]);
show_fan(input2, fan[1], fan_div[1]);
#define show_fan_div(suffix, value) \
static ssize_t show_fan_div##suffix(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct lm80_data *data = lm80_update_device(dev); \
return sprintf(buf, "%d\n", DIV_FROM_REG(data->value)); \
}
show_fan_div(1, fan_div[0]);
show_fan_div(2, fan_div[1]);
#define set_fan(suffix, value, reg, div) \
static ssize_t set_fan_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct lm80_data *data = i2c_get_clientdata(client); \
long val = simple_strtoul(buf, NULL, 10); \
\
down(&data->update_lock);\
data->value = FAN_TO_REG(val, DIV_FROM_REG(data->div)); \
lm80_write_value(client, reg, data->value); \
up(&data->update_lock);\
return count; \
}
set_fan(min1, fan_min[0], LM80_REG_FAN_MIN(1), fan_div[0]);
set_fan(min2, fan_min[1], LM80_REG_FAN_MIN(2), fan_div[1]);
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan divisor. This follows the principle of
least suprise; the user doesn't expect the fan minimum to change just
because the divisor changed. */
static ssize_t set_fan_div(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm80_data *data = i2c_get_clientdata(client);
unsigned long min, val = simple_strtoul(buf, NULL, 10);
u8 reg;
/* Save fan_min */
down(&data->update_lock);
min = FAN_FROM_REG(data->fan_min[nr],
DIV_FROM_REG(data->fan_div[nr]));
switch (val) {
case 1: data->fan_div[nr] = 0; break;
case 2: data->fan_div[nr] = 1; break;
case 4: data->fan_div[nr] = 2; break;
case 8: data->fan_div[nr] = 3; break;
default:
dev_err(&client->dev, "fan_div value %ld not "
"supported. Choose one of 1, 2, 4 or 8!\n", val);
up(&data->update_lock);
return -EINVAL;
}
reg = (lm80_read_value(client, LM80_REG_FANDIV) & ~(3 << (2 * (nr + 1))))
| (data->fan_div[nr] << (2 * (nr + 1)));
lm80_write_value(client, LM80_REG_FANDIV, reg);
/* Restore fan_min */
data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
lm80_write_value(client, LM80_REG_FAN_MIN(nr + 1), data->fan_min[nr]);
up(&data->update_lock);
return count;
}
#define set_fan_div(number) \
static ssize_t set_fan_div##number(struct device *dev, struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
return set_fan_div(dev, buf, count, number - 1); \
}
set_fan_div(1);
set_fan_div(2);
static ssize_t show_temp_input1(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm80_data *data = lm80_update_device(dev);
return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp));
}
#define show_temp(suffix, value) \
static ssize_t show_temp_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct lm80_data *data = lm80_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_LIMIT_FROM_REG(data->value)); \
}
show_temp(hot_max, temp_hot_max);
show_temp(hot_hyst, temp_hot_hyst);
show_temp(os_max, temp_os_max);
show_temp(os_hyst, temp_os_hyst);
#define set_temp(suffix, value, reg) \
static ssize_t set_temp_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct lm80_data *data = i2c_get_clientdata(client); \
long val = simple_strtoul(buf, NULL, 10); \
\
down(&data->update_lock); \
data->value = TEMP_LIMIT_TO_REG(val); \
lm80_write_value(client, reg, data->value); \
up(&data->update_lock); \
return count; \
}
set_temp(hot_max, temp_hot_max, LM80_REG_TEMP_HOT_MAX);
set_temp(hot_hyst, temp_hot_hyst, LM80_REG_TEMP_HOT_HYST);
set_temp(os_max, temp_os_max, LM80_REG_TEMP_OS_MAX);
set_temp(os_hyst, temp_os_hyst, LM80_REG_TEMP_OS_HYST);
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm80_data *data = lm80_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
static DEVICE_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min0, set_in_min0);
static DEVICE_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min1, set_in_min1);
static DEVICE_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min2, set_in_min2);
static DEVICE_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min3, set_in_min3);
static DEVICE_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min4, set_in_min4);
static DEVICE_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min5, set_in_min5);
static DEVICE_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min6, set_in_min6);
static DEVICE_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max0, set_in_max0);
static DEVICE_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max1, set_in_max1);
static DEVICE_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max2, set_in_max2);
static DEVICE_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max3, set_in_max3);
static DEVICE_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max4, set_in_max4);
static DEVICE_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max5, set_in_max5);
static DEVICE_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max6, set_in_max6);
static DEVICE_ATTR(in0_input, S_IRUGO, show_in_input0, NULL);
static DEVICE_ATTR(in1_input, S_IRUGO, show_in_input1, NULL);
static DEVICE_ATTR(in2_input, S_IRUGO, show_in_input2, NULL);
static DEVICE_ATTR(in3_input, S_IRUGO, show_in_input3, NULL);
static DEVICE_ATTR(in4_input, S_IRUGO, show_in_input4, NULL);
static DEVICE_ATTR(in5_input, S_IRUGO, show_in_input5, NULL);
static DEVICE_ATTR(in6_input, S_IRUGO, show_in_input6, NULL);
static DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min1,
set_fan_min1);
static DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min2,
set_fan_min2);
static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input1, NULL);
static DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input2, NULL);
static DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, show_fan_div1, set_fan_div1);
static DEVICE_ATTR(fan2_div, S_IWUSR | S_IRUGO, show_fan_div2, set_fan_div2);
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL);
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_hot_max,
set_temp_hot_max);
static DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_hot_hyst,
set_temp_hot_hyst);
static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp_os_max,
set_temp_os_max);
static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp_os_hyst,
set_temp_os_hyst);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
/*
* Real code
*/
static int lm80_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, lm80_detect);
}
int lm80_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i, cur;
struct i2c_client *new_client;
struct lm80_data *data;
int err = 0;
const char *name;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet.
But it allows us to access lm80_{read,write}_value. */
if (!(data = kmalloc(sizeof(struct lm80_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct lm80_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm80_driver;
new_client->flags = 0;
/* Now, we do the remaining detection. It is lousy. */
if (lm80_read_value(new_client, LM80_REG_ALARM2) & 0xc0)
goto error_free;
for (i = 0x2a; i <= 0x3d; i++) {
cur = i2c_smbus_read_byte_data(new_client, i);
if ((i2c_smbus_read_byte_data(new_client, i + 0x40) != cur)
|| (i2c_smbus_read_byte_data(new_client, i + 0x80) != cur)
|| (i2c_smbus_read_byte_data(new_client, i + 0xc0) != cur))
goto error_free;
}
/* Determine the chip type - only one kind supported! */
kind = lm80;
name = "lm80";
/* Fill in the remaining client fields and put it into the global list */
strlcpy(new_client->name, name, I2C_NAME_SIZE);
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 error_free;
/* Initialize the LM80 chip */
lm80_init_client(new_client);
/* A few vars need to be filled upon startup */
data->fan_min[0] = lm80_read_value(new_client, LM80_REG_FAN_MIN(1));
data->fan_min[1] = lm80_read_value(new_client, LM80_REG_FAN_MIN(2));
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_in0_min);
device_create_file(&new_client->dev, &dev_attr_in1_min);
device_create_file(&new_client->dev, &dev_attr_in2_min);
device_create_file(&new_client->dev, &dev_attr_in3_min);
device_create_file(&new_client->dev, &dev_attr_in4_min);
device_create_file(&new_client->dev, &dev_attr_in5_min);
device_create_file(&new_client->dev, &dev_attr_in6_min);
device_create_file(&new_client->dev, &dev_attr_in0_max);
device_create_file(&new_client->dev, &dev_attr_in1_max);
device_create_file(&new_client->dev, &dev_attr_in2_max);
device_create_file(&new_client->dev, &dev_attr_in3_max);
device_create_file(&new_client->dev, &dev_attr_in4_max);
device_create_file(&new_client->dev, &dev_attr_in5_max);
device_create_file(&new_client->dev, &dev_attr_in6_max);
device_create_file(&new_client->dev, &dev_attr_in0_input);
device_create_file(&new_client->dev, &dev_attr_in1_input);
device_create_file(&new_client->dev, &dev_attr_in2_input);
device_create_file(&new_client->dev, &dev_attr_in3_input);
device_create_file(&new_client->dev, &dev_attr_in4_input);
device_create_file(&new_client->dev, &dev_attr_in5_input);
device_create_file(&new_client->dev, &dev_attr_in6_input);
device_create_file(&new_client->dev, &dev_attr_fan1_min);
device_create_file(&new_client->dev, &dev_attr_fan2_min);
device_create_file(&new_client->dev, &dev_attr_fan1_input);
device_create_file(&new_client->dev, &dev_attr_fan2_input);
device_create_file(&new_client->dev, &dev_attr_fan1_div);
device_create_file(&new_client->dev, &dev_attr_fan2_div);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
device_create_file(&new_client->dev, &dev_attr_temp1_crit);
device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst);
device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
error_free:
kfree(data);
exit:
return err;
}
static int lm80_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;
}
kfree(i2c_get_clientdata(client));
return 0;
}
static int lm80_read_value(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
/* Called when we have found a new LM80. */
static void lm80_init_client(struct i2c_client *client)
{
/* Reset all except Watchdog values and last conversion values
This sets fan-divs to 2, among others. This makes most other
initializations unnecessary */
lm80_write_value(client, LM80_REG_CONFIG, 0x80);
/* Set 11-bit temperature resolution */
lm80_write_value(client, LM80_REG_RES, 0x08);
/* Start monitoring */
lm80_write_value(client, LM80_REG_CONFIG, 0x01);
}
static struct lm80_data *lm80_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm80_data *data = i2c_get_clientdata(client);
int i;
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) {
dev_dbg(&client->dev, "Starting lm80 update\n");
for (i = 0; i <= 6; i++) {
data->in[i] =
lm80_read_value(client, LM80_REG_IN(i));
data->in_min[i] =
lm80_read_value(client, LM80_REG_IN_MIN(i));
data->in_max[i] =
lm80_read_value(client, LM80_REG_IN_MAX(i));
}
data->fan[0] = lm80_read_value(client, LM80_REG_FAN1);
data->fan_min[0] =
lm80_read_value(client, LM80_REG_FAN_MIN(1));
data->fan[1] = lm80_read_value(client, LM80_REG_FAN2);
data->fan_min[1] =
lm80_read_value(client, LM80_REG_FAN_MIN(2));
data->temp =
(lm80_read_value(client, LM80_REG_TEMP) << 8) |
(lm80_read_value(client, LM80_REG_RES) & 0xf0);
data->temp_os_max =
lm80_read_value(client, LM80_REG_TEMP_OS_MAX);
data->temp_os_hyst =
lm80_read_value(client, LM80_REG_TEMP_OS_HYST);
data->temp_hot_max =
lm80_read_value(client, LM80_REG_TEMP_HOT_MAX);
data->temp_hot_hyst =
lm80_read_value(client, LM80_REG_TEMP_HOT_HYST);
i = lm80_read_value(client, LM80_REG_FANDIV);
data->fan_div[0] = (i >> 2) & 0x03;
data->fan_div[1] = (i >> 4) & 0x03;
data->alarms = lm80_read_value(client, LM80_REG_ALARM1) +
(lm80_read_value(client, LM80_REG_ALARM2) << 8);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_lm80_init(void)
{
return i2c_add_driver(&lm80_driver);
}
static void __exit sensors_lm80_exit(void)
{
i2c_del_driver(&lm80_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
"Philip Edelbrock <phil@netroedge.com>");
MODULE_DESCRIPTION("LM80 driver");
MODULE_LICENSE("GPL");
module_init(sensors_lm80_init);
module_exit(sensors_lm80_exit);

408
drivers/hwmon/lm83.c Normal file
Näytä tiedosto

@@ -0,0 +1,408 @@
/*
* lm83.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
* Copyright (C) 2003-2005 Jean Delvare <khali@linux-fr.org>
*
* Heavily inspired from the lm78, lm75 and adm1021 drivers. The LM83 is
* a sensor chip made by National Semiconductor. It reports up to four
* temperatures (its own plus up to three external ones) with a 1 deg
* resolution and a 3-4 deg accuracy. Complete datasheet can be obtained
* from National's website at:
* http://www.national.com/pf/LM/LM83.html
* Since the datasheet omits to give the chip stepping code, I give it
* here: 0x03 (at register 0xff).
*
* 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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/hwmon-sysfs.h>
/*
* Addresses to scan
* Address is selected using 2 three-level pins, resulting in 9 possible
* addresses.
*/
static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a,
0x29, 0x2a, 0x2b,
0x4c, 0x4d, 0x4e,
I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_1(lm83);
/*
* The LM83 registers
* Manufacturer ID is 0x01 for National Semiconductor.
*/
#define LM83_REG_R_MAN_ID 0xFE
#define LM83_REG_R_CHIP_ID 0xFF
#define LM83_REG_R_CONFIG 0x03
#define LM83_REG_W_CONFIG 0x09
#define LM83_REG_R_STATUS1 0x02
#define LM83_REG_R_STATUS2 0x35
#define LM83_REG_R_LOCAL_TEMP 0x00
#define LM83_REG_R_LOCAL_HIGH 0x05
#define LM83_REG_W_LOCAL_HIGH 0x0B
#define LM83_REG_R_REMOTE1_TEMP 0x30
#define LM83_REG_R_REMOTE1_HIGH 0x38
#define LM83_REG_W_REMOTE1_HIGH 0x50
#define LM83_REG_R_REMOTE2_TEMP 0x01
#define LM83_REG_R_REMOTE2_HIGH 0x07
#define LM83_REG_W_REMOTE2_HIGH 0x0D
#define LM83_REG_R_REMOTE3_TEMP 0x31
#define LM83_REG_R_REMOTE3_HIGH 0x3A
#define LM83_REG_W_REMOTE3_HIGH 0x52
#define LM83_REG_R_TCRIT 0x42
#define LM83_REG_W_TCRIT 0x5A
/*
* Conversions and various macros
* The LM83 uses signed 8-bit values with LSB = 1 degree Celsius.
*/
#define TEMP_FROM_REG(val) ((val) * 1000)
#define TEMP_TO_REG(val) ((val) <= -128000 ? -128 : \
(val) >= 127000 ? 127 : \
(val) < 0 ? ((val) - 500) / 1000 : \
((val) + 500) / 1000)
static const u8 LM83_REG_R_TEMP[] = {
LM83_REG_R_LOCAL_TEMP,
LM83_REG_R_REMOTE1_TEMP,
LM83_REG_R_REMOTE2_TEMP,
LM83_REG_R_REMOTE3_TEMP,
LM83_REG_R_LOCAL_HIGH,
LM83_REG_R_REMOTE1_HIGH,
LM83_REG_R_REMOTE2_HIGH,
LM83_REG_R_REMOTE3_HIGH,
LM83_REG_R_TCRIT,
};
static const u8 LM83_REG_W_HIGH[] = {
LM83_REG_W_LOCAL_HIGH,
LM83_REG_W_REMOTE1_HIGH,
LM83_REG_W_REMOTE2_HIGH,
LM83_REG_W_REMOTE3_HIGH,
LM83_REG_W_TCRIT,
};
/*
* Functions declaration
*/
static int lm83_attach_adapter(struct i2c_adapter *adapter);
static int lm83_detect(struct i2c_adapter *adapter, int address, int kind);
static int lm83_detach_client(struct i2c_client *client);
static struct lm83_data *lm83_update_device(struct device *dev);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver lm83_driver = {
.owner = THIS_MODULE,
.name = "lm83",
.id = I2C_DRIVERID_LM83,
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm83_attach_adapter,
.detach_client = lm83_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct lm83_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
/* registers values */
s8 temp[9]; /* 0..3: input 1-4,
4..7: high limit 1-4,
8 : critical limit */
u16 alarms; /* bitvector, combined */
};
/*
* Sysfs stuff
*/
static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm83_data *data = lm83_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index]));
}
static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct i2c_client *client = to_i2c_client(dev);
struct lm83_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
int nr = attr->index;
down(&data->update_lock);
data->temp[nr] = TEMP_TO_REG(val);
i2c_smbus_write_byte_data(client, LM83_REG_W_HIGH[nr - 4],
data->temp[nr]);
up(&data->update_lock);
return count;
}
static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy,
char *buf)
{
struct lm83_data *data = lm83_update_device(dev);
return sprintf(buf, "%d\n", data->alarms);
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp,
set_temp, 4);
static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp,
set_temp, 5);
static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_temp,
set_temp, 6);
static SENSOR_DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_temp,
set_temp, 7);
static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, NULL, 8);
static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp, NULL, 8);
static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_temp,
set_temp, 8);
static SENSOR_DEVICE_ATTR(temp4_crit, S_IRUGO, show_temp, NULL, 8);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
/*
* Real code
*/
static int lm83_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, lm83_detect);
}
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
*/
static int lm83_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct lm83_data *data;
int err = 0;
const char *name = "";
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct lm83_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct lm83_data));
/* The common I2C client data is placed right after the
* LM83-specific data. */
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm83_driver;
new_client->flags = 0;
/* Now we do the detection and identification. A negative kind
* means that the driver was loaded with no force parameter
* (default), so we must both detect and identify the chip
* (actually there is only one possible kind of chip for now, LM83).
* A zero kind means that the driver was loaded with the force
* parameter, the detection step shall be skipped. A positive kind
* means that the driver was loaded with the force parameter and a
* given kind of chip is requested, so both the detection and the
* identification steps are skipped. */
/* Default to an LM83 if forced */
if (kind == 0)
kind = lm83;
if (kind < 0) { /* detection */
if (((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS1)
& 0xA8) != 0x00) ||
((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS2)
& 0x48) != 0x00) ||
((i2c_smbus_read_byte_data(new_client, LM83_REG_R_CONFIG)
& 0x41) != 0x00)) {
dev_dbg(&adapter->dev,
"LM83 detection failed at 0x%02x.\n", address);
goto exit_free;
}
}
if (kind <= 0) { /* identification */
u8 man_id, chip_id;
man_id = i2c_smbus_read_byte_data(new_client,
LM83_REG_R_MAN_ID);
chip_id = i2c_smbus_read_byte_data(new_client,
LM83_REG_R_CHIP_ID);
if (man_id == 0x01) { /* National Semiconductor */
if (chip_id == 0x03) {
kind = lm83;
}
}
if (kind <= 0) { /* identification failed */
dev_info(&adapter->dev,
"Unsupported chip (man_id=0x%02X, "
"chip_id=0x%02X).\n", man_id, chip_id);
goto exit_free;
}
}
if (kind == lm83) {
name = "lm83";
}
/* We can fill in the remaining client fields */
strlcpy(new_client->name, name, I2C_NAME_SIZE);
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 exit_free;
/*
* Initialize the LM83 chip
* (Nothing to do for this one.)
*/
/* Register sysfs hooks */
device_create_file(&new_client->dev,
&sensor_dev_attr_temp1_input.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp2_input.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp3_input.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp4_input.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp1_max.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp2_max.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp3_max.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp4_max.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp1_crit.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp2_crit.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp3_crit.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp4_crit.dev_attr);
device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static int lm83_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;
}
kfree(i2c_get_clientdata(client));
return 0;
}
static struct lm83_data *lm83_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm83_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
int nr;
dev_dbg(&client->dev, "Updating lm83 data.\n");
for (nr = 0; nr < 9; nr++) {
data->temp[nr] =
i2c_smbus_read_byte_data(client,
LM83_REG_R_TEMP[nr]);
}
data->alarms =
i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS1)
+ (i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS2)
<< 8);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_lm83_init(void)
{
return i2c_add_driver(&lm83_driver);
}
static void __exit sensors_lm83_exit(void)
{
i2c_del_driver(&lm83_driver);
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("LM83 driver");
MODULE_LICENSE("GPL");
module_init(sensors_lm83_init);
module_exit(sensors_lm83_exit);

1575
drivers/hwmon/lm85.c Normal file

File diff suppressed because it is too large Load Diff

828
drivers/hwmon/lm87.c Normal file
Näytä tiedosto

@@ -0,0 +1,828 @@
/*
* lm87.c
*
* Copyright (C) 2000 Frodo Looijaard <frodol@dds.nl>
* Philip Edelbrock <phil@netroedge.com>
* Stephen Rousset <stephen.rousset@rocketlogix.com>
* Dan Eaton <dan.eaton@rocketlogix.com>
* Copyright (C) 2004 Jean Delvare <khali@linux-fr.org>
*
* Original port to Linux 2.6 by Jeff Oliver.
*
* The LM87 is a sensor chip made by National Semiconductor. It monitors up
* to 8 voltages (including its own power source), up to three temperatures
* (its own plus up to two external ones) and up to two fans. The default
* configuration is 6 voltages, two temperatures and two fans (see below).
* Voltages are scaled internally with ratios such that the nominal value of
* each voltage correspond to a register value of 192 (which means a
* resolution of about 0.5% of the nominal value). Temperature values are
* reported with a 1 deg resolution and a 3-4 deg accuracy. Complete
* datasheet can be obtained from National's website at:
* http://www.national.com/pf/LM/LM87.html
*
* Some functions share pins, so not all functions are available at the same
* time. Which are depends on the hardware setup. This driver assumes that
* the BIOS configured the chip correctly. In that respect, it differs from
* the original driver (from lm_sensors for Linux 2.4), which would force the
* LM87 to an arbitrary, compile-time chosen mode, regardless of the actual
* chipset wiring.
* For reference, here is the list of exclusive functions:
* - in0+in5 (default) or temp3
* - fan1 (default) or in6
* - fan2 (default) or in7
* - VID lines (default) or IRQ lines (not handled by this driver)
*
* The LM87 additionally features an analog output, supposedly usable to
* control the speed of a fan. All new chips use pulse width modulation
* instead. The LM87 is the only hardware monitoring chipset I know of
* which uses amplitude modulation. Be careful when using this feature.
*
* 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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/i2c-vid.h>
/*
* Addresses to scan
* LM87 has three possible addresses: 0x2c, 0x2d and 0x2e.
*/
static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_1(lm87);
/*
* The LM87 registers
*/
/* nr in 0..5 */
#define LM87_REG_IN(nr) (0x20 + (nr))
#define LM87_REG_IN_MAX(nr) (0x2B + (nr) * 2)
#define LM87_REG_IN_MIN(nr) (0x2C + (nr) * 2)
/* nr in 0..1 */
#define LM87_REG_AIN(nr) (0x28 + (nr))
#define LM87_REG_AIN_MIN(nr) (0x1A + (nr))
#define LM87_REG_AIN_MAX(nr) (0x3B + (nr))
static u8 LM87_REG_TEMP[3] = { 0x27, 0x26, 0x20 };
static u8 LM87_REG_TEMP_HIGH[3] = { 0x39, 0x37, 0x2B };
static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C };
#define LM87_REG_TEMP_HW_INT_LOCK 0x13
#define LM87_REG_TEMP_HW_EXT_LOCK 0x14
#define LM87_REG_TEMP_HW_INT 0x17
#define LM87_REG_TEMP_HW_EXT 0x18
/* nr in 0..1 */
#define LM87_REG_FAN(nr) (0x28 + (nr))
#define LM87_REG_FAN_MIN(nr) (0x3B + (nr))
#define LM87_REG_AOUT 0x19
#define LM87_REG_CONFIG 0x40
#define LM87_REG_CHANNEL_MODE 0x16
#define LM87_REG_VID_FAN_DIV 0x47
#define LM87_REG_VID4 0x49
#define LM87_REG_ALARMS1 0x41
#define LM87_REG_ALARMS2 0x42
#define LM87_REG_COMPANY_ID 0x3E
#define LM87_REG_REVISION 0x3F
/*
* Conversions and various macros
* The LM87 uses signed 8-bit values for temperatures.
*/
#define IN_FROM_REG(reg,scale) (((reg) * (scale) + 96) / 192)
#define IN_TO_REG(val,scale) ((val) <= 0 ? 0 : \
(val) * 192 >= (scale) * 255 ? 255 : \
((val) * 192 + (scale)/2) / (scale))
#define TEMP_FROM_REG(reg) ((reg) * 1000)
#define TEMP_TO_REG(val) ((val) <= -127500 ? -128 : \
(val) >= 126500 ? 127 : \
(((val) < 0 ? (val)-500 : (val)+500) / 1000))
#define FAN_FROM_REG(reg,div) ((reg) == 255 || (reg) == 0 ? 0 : \
1350000 + (reg)*(div) / 2) / ((reg)*(div))
#define FAN_TO_REG(val,div) ((val)*(div) * 255 <= 1350000 ? 255 : \
(1350000 + (val)*(div) / 2) / ((val)*(div)))
#define FAN_DIV_FROM_REG(reg) (1 << (reg))
/* analog out is 9.80mV/LSB */
#define AOUT_FROM_REG(reg) (((reg) * 98 + 5) / 10)
#define AOUT_TO_REG(val) ((val) <= 0 ? 0 : \
(val) >= 2500 ? 255 : \
((val) * 10 + 49) / 98)
/* nr in 0..1 */
#define CHAN_NO_FAN(nr) (1 << (nr))
#define CHAN_TEMP3 (1 << 2)
#define CHAN_VCC_5V (1 << 3)
#define CHAN_NO_VID (1 << 8)
/*
* Functions declaration
*/
static int lm87_attach_adapter(struct i2c_adapter *adapter);
static int lm87_detect(struct i2c_adapter *adapter, int address, int kind);
static void lm87_init_client(struct i2c_client *client);
static int lm87_detach_client(struct i2c_client *client);
static struct lm87_data *lm87_update_device(struct device *dev);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver lm87_driver = {
.owner = THIS_MODULE,
.name = "lm87",
.id = I2C_DRIVERID_LM87,
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm87_attach_adapter,
.detach_client = lm87_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct lm87_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* In jiffies */
u8 channel; /* register value */
u8 in[8]; /* register value */
u8 in_max[8]; /* register value */
u8 in_min[8]; /* register value */
u16 in_scale[8];
s8 temp[3]; /* register value */
s8 temp_high[3]; /* register value */
s8 temp_low[3]; /* register value */
s8 temp_crit_int; /* min of two register values */
s8 temp_crit_ext; /* min of two register values */
u8 fan[2]; /* register value */
u8 fan_min[2]; /* register value */
u8 fan_div[2]; /* register value, shifted right */
u8 aout; /* register value */
u16 alarms; /* register values, combined */
u8 vid; /* register values, combined */
u8 vrm;
};
/*
* Sysfs stuff
*/
static inline int lm87_read_value(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
static inline int lm87_write_value(struct i2c_client *client, u8 reg, u8 value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
#define show_in(offset) \
static ssize_t show_in##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct lm87_data *data = lm87_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \
data->in_scale[offset])); \
} \
static ssize_t show_in##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct lm87_data *data = lm87_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \
data->in_scale[offset])); \
} \
static ssize_t show_in##offset##_max(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct lm87_data *data = lm87_update_device(dev); \
return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \
data->in_scale[offset])); \
} \
static DEVICE_ATTR(in##offset##_input, S_IRUGO, \
show_in##offset##_input, NULL);
show_in(0);
show_in(1);
show_in(2);
show_in(3);
show_in(4);
show_in(5);
show_in(6);
show_in(7);
static void set_in_min(struct device *dev, const char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm87_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->in_min[nr] = IN_TO_REG(val, data->in_scale[nr]);
lm87_write_value(client, nr<6 ? LM87_REG_IN_MIN(nr) :
LM87_REG_AIN_MIN(nr-6), data->in_min[nr]);
up(&data->update_lock);
}
static void set_in_max(struct device *dev, const char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm87_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->in_max[nr] = IN_TO_REG(val, data->in_scale[nr]);
lm87_write_value(client, nr<6 ? LM87_REG_IN_MAX(nr) :
LM87_REG_AIN_MAX(nr-6), data->in_max[nr]);
up(&data->update_lock);
}
#define set_in(offset) \
static ssize_t set_in##offset##_min(struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
set_in_min(dev, buf, offset); \
return count; \
} \
static ssize_t set_in##offset##_max(struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
set_in_max(dev, buf, offset); \
return count; \
} \
static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
show_in##offset##_min, set_in##offset##_min); \
static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
show_in##offset##_max, set_in##offset##_max);
set_in(0);
set_in(1);
set_in(2);
set_in(3);
set_in(4);
set_in(5);
set_in(6);
set_in(7);
#define show_temp(offset) \
static ssize_t show_temp##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct lm87_data *data = lm87_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[offset-1])); \
} \
static ssize_t show_temp##offset##_low(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct lm87_data *data = lm87_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_low[offset-1])); \
} \
static ssize_t show_temp##offset##_high(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct lm87_data *data = lm87_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[offset-1])); \
}\
static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \
show_temp##offset##_input, NULL);
show_temp(1);
show_temp(2);
show_temp(3);
static void set_temp_low(struct device *dev, const char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm87_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_low[nr] = TEMP_TO_REG(val);
lm87_write_value(client, LM87_REG_TEMP_LOW[nr], data->temp_low[nr]);
up(&data->update_lock);
}
static void set_temp_high(struct device *dev, const char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm87_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_high[nr] = TEMP_TO_REG(val);
lm87_write_value(client, LM87_REG_TEMP_HIGH[nr], data->temp_high[nr]);
up(&data->update_lock);
}
#define set_temp(offset) \
static ssize_t set_temp##offset##_low(struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
set_temp_low(dev, buf, offset-1); \
return count; \
} \
static ssize_t set_temp##offset##_high(struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
set_temp_high(dev, buf, offset-1); \
return count; \
} \
static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \
show_temp##offset##_high, set_temp##offset##_high); \
static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \
show_temp##offset##_low, set_temp##offset##_low);
set_temp(1);
set_temp(2);
set_temp(3);
static ssize_t show_temp_crit_int(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_int));
}
static ssize_t show_temp_crit_ext(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_ext));
}
static DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit_int, NULL);
static DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp_crit_ext, NULL);
static DEVICE_ATTR(temp3_crit, S_IRUGO, show_temp_crit_ext, NULL);
#define show_fan(offset) \
static ssize_t show_fan##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct lm87_data *data = lm87_update_device(dev); \
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[offset-1], \
FAN_DIV_FROM_REG(data->fan_div[offset-1]))); \
} \
static ssize_t show_fan##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct lm87_data *data = lm87_update_device(dev); \
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[offset-1], \
FAN_DIV_FROM_REG(data->fan_div[offset-1]))); \
} \
static ssize_t show_fan##offset##_div(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct lm87_data *data = lm87_update_device(dev); \
return sprintf(buf, "%d\n", FAN_DIV_FROM_REG(data->fan_div[offset-1])); \
} \
static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \
show_fan##offset##_input, NULL);
show_fan(1);
show_fan(2);
static void set_fan_min(struct device *dev, const char *buf, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm87_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->fan_min[nr] = FAN_TO_REG(val,
FAN_DIV_FROM_REG(data->fan_div[nr]));
lm87_write_value(client, LM87_REG_FAN_MIN(nr), data->fan_min[nr]);
up(&data->update_lock);
}
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan clock divider. This follows the principle
of least suprise; the user doesn't expect the fan minimum to change just
because the divider changed. */
static ssize_t set_fan_div(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm87_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
unsigned long min;
u8 reg;
down(&data->update_lock);
min = FAN_FROM_REG(data->fan_min[nr],
FAN_DIV_FROM_REG(data->fan_div[nr]));
switch (val) {
case 1: data->fan_div[nr] = 0; break;
case 2: data->fan_div[nr] = 1; break;
case 4: data->fan_div[nr] = 2; break;
case 8: data->fan_div[nr] = 3; break;
default:
up(&data->update_lock);
return -EINVAL;
}
reg = lm87_read_value(client, LM87_REG_VID_FAN_DIV);
switch (nr) {
case 0:
reg = (reg & 0xCF) | (data->fan_div[0] << 4);
break;
case 1:
reg = (reg & 0x3F) | (data->fan_div[1] << 6);
break;
}
lm87_write_value(client, LM87_REG_VID_FAN_DIV, reg);
data->fan_min[nr] = FAN_TO_REG(min, val);
lm87_write_value(client, LM87_REG_FAN_MIN(nr),
data->fan_min[nr]);
up(&data->update_lock);
return count;
}
#define set_fan(offset) \
static ssize_t set_fan##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
set_fan_min(dev, buf, offset-1); \
return count; \
} \
static ssize_t set_fan##offset##_div(struct device *dev, struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
return set_fan_div(dev, buf, count, offset-1); \
} \
static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
show_fan##offset##_min, set_fan##offset##_min); \
static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
show_fan##offset##_div, set_fan##offset##_div);
set_fan(1);
set_fan(2);
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
return sprintf(buf, "%d\n", data->alarms);
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
}
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
return sprintf(buf, "%d\n", data->vrm);
}
static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm87_data *data = i2c_get_clientdata(client);
data->vrm = simple_strtoul(buf, NULL, 10);
return count;
}
static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
static ssize_t show_aout(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm87_data *data = lm87_update_device(dev);
return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout));
}
static ssize_t set_aout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm87_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->aout = AOUT_TO_REG(val);
lm87_write_value(client, LM87_REG_AOUT, data->aout);
up(&data->update_lock);
return count;
}
static DEVICE_ATTR(aout_output, S_IRUGO | S_IWUSR, show_aout, set_aout);
/*
* Real code
*/
static int lm87_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, lm87_detect);
}
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
*/
static int lm87_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct lm87_data *data;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct lm87_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct lm87_data));
/* The common I2C client data is placed right before the
LM87-specific data. */
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm87_driver;
new_client->flags = 0;
/* Default to an LM87 if forced */
if (kind == 0)
kind = lm87;
/* Now, we do the remaining detection. */
if (kind < 0) {
u8 rev = lm87_read_value(new_client, LM87_REG_REVISION);
if (rev < 0x01 || rev > 0x08
|| (lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80)
|| lm87_read_value(new_client, LM87_REG_COMPANY_ID) != 0x02) {
dev_dbg(&adapter->dev,
"LM87 detection failed at 0x%02x.\n",
address);
goto exit_free;
}
}
/* We can fill in the remaining client fields */
strlcpy(new_client->name, "lm87", I2C_NAME_SIZE);
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 exit_free;
/* Initialize the LM87 chip */
lm87_init_client(new_client);
data->in_scale[0] = 2500;
data->in_scale[1] = 2700;
data->in_scale[2] = (data->channel & CHAN_VCC_5V) ? 5000 : 3300;
data->in_scale[3] = 5000;
data->in_scale[4] = 12000;
data->in_scale[5] = 2700;
data->in_scale[6] = 1875;
data->in_scale[7] = 1875;
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_in1_input);
device_create_file(&new_client->dev, &dev_attr_in1_min);
device_create_file(&new_client->dev, &dev_attr_in1_max);
device_create_file(&new_client->dev, &dev_attr_in2_input);
device_create_file(&new_client->dev, &dev_attr_in2_min);
device_create_file(&new_client->dev, &dev_attr_in2_max);
device_create_file(&new_client->dev, &dev_attr_in3_input);
device_create_file(&new_client->dev, &dev_attr_in3_min);
device_create_file(&new_client->dev, &dev_attr_in3_max);
device_create_file(&new_client->dev, &dev_attr_in4_input);
device_create_file(&new_client->dev, &dev_attr_in4_min);
device_create_file(&new_client->dev, &dev_attr_in4_max);
if (data->channel & CHAN_NO_FAN(0)) {
device_create_file(&new_client->dev, &dev_attr_in6_input);
device_create_file(&new_client->dev, &dev_attr_in6_min);
device_create_file(&new_client->dev, &dev_attr_in6_max);
} else {
device_create_file(&new_client->dev, &dev_attr_fan1_input);
device_create_file(&new_client->dev, &dev_attr_fan1_min);
device_create_file(&new_client->dev, &dev_attr_fan1_div);
}
if (data->channel & CHAN_NO_FAN(1)) {
device_create_file(&new_client->dev, &dev_attr_in7_input);
device_create_file(&new_client->dev, &dev_attr_in7_min);
device_create_file(&new_client->dev, &dev_attr_in7_max);
} else {
device_create_file(&new_client->dev, &dev_attr_fan2_input);
device_create_file(&new_client->dev, &dev_attr_fan2_min);
device_create_file(&new_client->dev, &dev_attr_fan2_div);
}
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_min);
device_create_file(&new_client->dev, &dev_attr_temp1_crit);
device_create_file(&new_client->dev, &dev_attr_temp2_input);
device_create_file(&new_client->dev, &dev_attr_temp2_max);
device_create_file(&new_client->dev, &dev_attr_temp2_min);
device_create_file(&new_client->dev, &dev_attr_temp2_crit);
if (data->channel & CHAN_TEMP3) {
device_create_file(&new_client->dev, &dev_attr_temp3_input);
device_create_file(&new_client->dev, &dev_attr_temp3_max);
device_create_file(&new_client->dev, &dev_attr_temp3_min);
device_create_file(&new_client->dev, &dev_attr_temp3_crit);
} else {
device_create_file(&new_client->dev, &dev_attr_in0_input);
device_create_file(&new_client->dev, &dev_attr_in0_min);
device_create_file(&new_client->dev, &dev_attr_in0_max);
device_create_file(&new_client->dev, &dev_attr_in5_input);
device_create_file(&new_client->dev, &dev_attr_in5_min);
device_create_file(&new_client->dev, &dev_attr_in5_max);
}
if (!(data->channel & CHAN_NO_VID)) {
device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
device_create_file(&new_client->dev, &dev_attr_vrm);
}
device_create_file(&new_client->dev, &dev_attr_alarms);
device_create_file(&new_client->dev, &dev_attr_aout_output);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static void lm87_init_client(struct i2c_client *client)
{
struct lm87_data *data = i2c_get_clientdata(client);
u8 config;
data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE);
data->vrm = i2c_which_vrm();
config = lm87_read_value(client, LM87_REG_CONFIG);
if (!(config & 0x01)) {
int i;
/* Limits are left uninitialized after power-up */
for (i = 1; i < 6; i++) {
lm87_write_value(client, LM87_REG_IN_MIN(i), 0x00);
lm87_write_value(client, LM87_REG_IN_MAX(i), 0xFF);
}
for (i = 0; i < 2; i++) {
lm87_write_value(client, LM87_REG_TEMP_HIGH[i], 0x7F);
lm87_write_value(client, LM87_REG_TEMP_LOW[i], 0x00);
lm87_write_value(client, LM87_REG_AIN_MIN(i), 0x00);
lm87_write_value(client, LM87_REG_AIN_MAX(i), 0xFF);
}
if (data->channel & CHAN_TEMP3) {
lm87_write_value(client, LM87_REG_TEMP_HIGH[2], 0x7F);
lm87_write_value(client, LM87_REG_TEMP_LOW[2], 0x00);
} else {
lm87_write_value(client, LM87_REG_IN_MIN(0), 0x00);
lm87_write_value(client, LM87_REG_IN_MAX(0), 0xFF);
}
}
if ((config & 0x81) != 0x01) {
/* Start monitoring */
lm87_write_value(client, LM87_REG_CONFIG,
(config & 0xF7) | 0x01);
}
}
static int lm87_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;
}
kfree(i2c_get_clientdata(client));
return 0;
}
static struct lm87_data *lm87_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm87_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
int i, j;
dev_dbg(&client->dev, "Updating data.\n");
i = (data->channel & CHAN_TEMP3) ? 1 : 0;
j = (data->channel & CHAN_TEMP3) ? 5 : 6;
for (; i < j; i++) {
data->in[i] = lm87_read_value(client,
LM87_REG_IN(i));
data->in_min[i] = lm87_read_value(client,
LM87_REG_IN_MIN(i));
data->in_max[i] = lm87_read_value(client,
LM87_REG_IN_MAX(i));
}
for (i = 0; i < 2; i++) {
if (data->channel & CHAN_NO_FAN(i)) {
data->in[6+i] = lm87_read_value(client,
LM87_REG_AIN(i));
data->in_max[6+i] = lm87_read_value(client,
LM87_REG_AIN_MAX(i));
data->in_min[6+i] = lm87_read_value(client,
LM87_REG_AIN_MIN(i));
} else {
data->fan[i] = lm87_read_value(client,
LM87_REG_FAN(i));
data->fan_min[i] = lm87_read_value(client,
LM87_REG_FAN_MIN(i));
}
}
j = (data->channel & CHAN_TEMP3) ? 3 : 2;
for (i = 0 ; i < j; i++) {
data->temp[i] = lm87_read_value(client,
LM87_REG_TEMP[i]);
data->temp_high[i] = lm87_read_value(client,
LM87_REG_TEMP_HIGH[i]);
data->temp_low[i] = lm87_read_value(client,
LM87_REG_TEMP_LOW[i]);
}
i = lm87_read_value(client, LM87_REG_TEMP_HW_INT_LOCK);
j = lm87_read_value(client, LM87_REG_TEMP_HW_INT);
data->temp_crit_int = min(i, j);
i = lm87_read_value(client, LM87_REG_TEMP_HW_EXT_LOCK);
j = lm87_read_value(client, LM87_REG_TEMP_HW_EXT);
data->temp_crit_ext = min(i, j);
i = lm87_read_value(client, LM87_REG_VID_FAN_DIV);
data->fan_div[0] = (i >> 4) & 0x03;
data->fan_div[1] = (i >> 6) & 0x03;
data->vid = (i & 0x0F)
| (lm87_read_value(client, LM87_REG_VID4) & 0x01)
<< 4;
data->alarms = lm87_read_value(client, LM87_REG_ALARMS1)
| (lm87_read_value(client, LM87_REG_ALARMS2)
<< 8);
data->aout = lm87_read_value(client, LM87_REG_AOUT);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_lm87_init(void)
{
return i2c_add_driver(&lm87_driver);
}
static void __exit sensors_lm87_exit(void)
{
i2c_del_driver(&lm87_driver);
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org> and others");
MODULE_DESCRIPTION("LM87 driver");
MODULE_LICENSE("GPL");
module_init(sensors_lm87_init);
module_exit(sensors_lm87_exit);

655
drivers/hwmon/lm90.c Normal file
Näytä tiedosto

@@ -0,0 +1,655 @@
/*
* lm90.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
* Copyright (C) 2003-2005 Jean Delvare <khali@linux-fr.org>
*
* Based on the lm83 driver. The LM90 is a sensor chip made by National
* Semiconductor. It reports up to two temperatures (its own plus up to
* one external one) with a 0.125 deg resolution (1 deg for local
* temperature) and a 3-4 deg accuracy. Complete datasheet can be
* obtained from National's website at:
* http://www.national.com/pf/LM/LM90.html
*
* This driver also supports the LM89 and LM99, two other sensor chips
* made by National Semiconductor. Both have an increased remote
* temperature measurement accuracy (1 degree), and the LM99
* additionally shifts remote temperatures (measured and limits) by 16
* degrees, which allows for higher temperatures measurement. The
* driver doesn't handle it since it can be done easily in user-space.
* Complete datasheets can be obtained from National's website at:
* http://www.national.com/pf/LM/LM89.html
* http://www.national.com/pf/LM/LM99.html
* Note that there is no way to differentiate between both chips.
*
* This driver also supports the LM86, another sensor chip made by
* National Semiconductor. It is exactly similar to the LM90 except it
* has a higher accuracy.
* Complete datasheet can be obtained from National's website at:
* http://www.national.com/pf/LM/LM86.html
*
* This driver also supports the ADM1032, a sensor chip made by Analog
* Devices. That chip is similar to the LM90, with a few differences
* that are not handled by this driver. Complete datasheet can be
* obtained from Analog's website at:
* http://products.analog.com/products/info.asp?product=ADM1032
* Among others, it has a higher accuracy than the LM90, much like the
* LM86 does.
*
* This driver also supports the MAX6657, MAX6658 and MAX6659 sensor
* chips made by Maxim. These chips are similar to the LM86. Complete
* datasheet can be obtained at Maxim's website at:
* http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578
* Note that there is no easy way to differentiate between the three
* variants. The extra address and features of the MAX6659 are not
* supported by this driver.
*
* This driver also supports the ADT7461 chip from Analog Devices but
* only in its "compatability mode". If an ADT7461 chip is found but
* is configured in non-compatible mode (where its temperature
* register values are decoded differently) it is ignored by this
* driver. Complete datasheet can be obtained from Analog's website
* at:
* http://products.analog.com/products/info.asp?product=ADT7461
*
* Since the LM90 was the first chipset supported by this driver, most
* comments will refer to this chipset, but are actually general and
* concern all supported chipsets, unless mentioned otherwise.
*
* 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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/hwmon-sysfs.h>
/*
* Addresses to scan
* Address is fully defined internally and cannot be changed except for
* MAX6659.
* LM86, LM89, LM90, LM99, ADM1032, MAX6657 and MAX6658 have address 0x4c.
* LM89-1, and LM99-1 have address 0x4d.
* MAX6659 can have address 0x4c, 0x4d or 0x4e (unsupported).
* ADT7461 always has address 0x4c.
*/
static unsigned short normal_i2c[] = { 0x4c, 0x4d, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_6(lm90, adm1032, lm99, lm86, max6657, adt7461);
/*
* The LM90 registers
*/
#define LM90_REG_R_MAN_ID 0xFE
#define LM90_REG_R_CHIP_ID 0xFF
#define LM90_REG_R_CONFIG1 0x03
#define LM90_REG_W_CONFIG1 0x09
#define LM90_REG_R_CONFIG2 0xBF
#define LM90_REG_W_CONFIG2 0xBF
#define LM90_REG_R_CONVRATE 0x04
#define LM90_REG_W_CONVRATE 0x0A
#define LM90_REG_R_STATUS 0x02
#define LM90_REG_R_LOCAL_TEMP 0x00
#define LM90_REG_R_LOCAL_HIGH 0x05
#define LM90_REG_W_LOCAL_HIGH 0x0B
#define LM90_REG_R_LOCAL_LOW 0x06
#define LM90_REG_W_LOCAL_LOW 0x0C
#define LM90_REG_R_LOCAL_CRIT 0x20
#define LM90_REG_W_LOCAL_CRIT 0x20
#define LM90_REG_R_REMOTE_TEMPH 0x01
#define LM90_REG_R_REMOTE_TEMPL 0x10
#define LM90_REG_R_REMOTE_OFFSH 0x11
#define LM90_REG_W_REMOTE_OFFSH 0x11
#define LM90_REG_R_REMOTE_OFFSL 0x12
#define LM90_REG_W_REMOTE_OFFSL 0x12
#define LM90_REG_R_REMOTE_HIGHH 0x07
#define LM90_REG_W_REMOTE_HIGHH 0x0D
#define LM90_REG_R_REMOTE_HIGHL 0x13
#define LM90_REG_W_REMOTE_HIGHL 0x13
#define LM90_REG_R_REMOTE_LOWH 0x08
#define LM90_REG_W_REMOTE_LOWH 0x0E
#define LM90_REG_R_REMOTE_LOWL 0x14
#define LM90_REG_W_REMOTE_LOWL 0x14
#define LM90_REG_R_REMOTE_CRIT 0x19
#define LM90_REG_W_REMOTE_CRIT 0x19
#define LM90_REG_R_TCRIT_HYST 0x21
#define LM90_REG_W_TCRIT_HYST 0x21
/*
* Conversions and various macros
* For local temperatures and limits, critical limits and the hysteresis
* value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius.
* For remote temperatures and limits, it uses signed 11-bit values with
* LSB = 0.125 degree Celsius, left-justified in 16-bit registers.
*/
#define TEMP1_FROM_REG(val) ((val) * 1000)
#define TEMP1_TO_REG(val) ((val) <= -128000 ? -128 : \
(val) >= 127000 ? 127 : \
(val) < 0 ? ((val) - 500) / 1000 : \
((val) + 500) / 1000)
#define TEMP2_FROM_REG(val) ((val) / 32 * 125)
#define TEMP2_TO_REG(val) ((val) <= -128000 ? 0x8000 : \
(val) >= 127875 ? 0x7FE0 : \
(val) < 0 ? ((val) - 62) / 125 * 32 : \
((val) + 62) / 125 * 32)
#define HYST_TO_REG(val) ((val) <= 0 ? 0 : (val) >= 30500 ? 31 : \
((val) + 500) / 1000)
/*
* ADT7461 is almost identical to LM90 except that attempts to write
* values that are outside the range 0 < temp < 127 are treated as
* the boundary value.
*/
#define TEMP1_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \
(val) >= 127000 ? 127 : \
((val) + 500) / 1000)
#define TEMP2_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \
(val) >= 127750 ? 0x7FC0 : \
((val) + 125) / 250 * 64)
/*
* Functions declaration
*/
static int lm90_attach_adapter(struct i2c_adapter *adapter);
static int lm90_detect(struct i2c_adapter *adapter, int address,
int kind);
static void lm90_init_client(struct i2c_client *client);
static int lm90_detach_client(struct i2c_client *client);
static struct lm90_data *lm90_update_device(struct device *dev);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver lm90_driver = {
.owner = THIS_MODULE,
.name = "lm90",
.id = I2C_DRIVERID_LM90,
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm90_attach_adapter,
.detach_client = lm90_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct lm90_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
int kind;
/* registers values */
s8 temp8[5]; /* 0: local input
1: local low limit
2: local high limit
3: local critical limit
4: remote critical limit */
s16 temp11[3]; /* 0: remote input
1: remote low limit
2: remote high limit */
u8 temp_hyst;
u8 alarms; /* bitvector */
};
/*
* Sysfs stuff
*/
static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm90_data *data = lm90_update_device(dev);
return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp8[attr->index]));
}
static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
static const u8 reg[4] = {
LM90_REG_W_LOCAL_LOW,
LM90_REG_W_LOCAL_HIGH,
LM90_REG_W_LOCAL_CRIT,
LM90_REG_W_REMOTE_CRIT,
};
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct i2c_client *client = to_i2c_client(dev);
struct lm90_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
int nr = attr->index;
down(&data->update_lock);
if (data->kind == adt7461)
data->temp8[nr] = TEMP1_TO_REG_ADT7461(val);
else
data->temp8[nr] = TEMP1_TO_REG(val);
i2c_smbus_write_byte_data(client, reg[nr - 1], data->temp8[nr]);
up(&data->update_lock);
return count;
}
static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm90_data *data = lm90_update_device(dev);
return sprintf(buf, "%d\n", TEMP2_FROM_REG(data->temp11[attr->index]));
}
static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
static const u8 reg[4] = {
LM90_REG_W_REMOTE_LOWH,
LM90_REG_W_REMOTE_LOWL,
LM90_REG_W_REMOTE_HIGHH,
LM90_REG_W_REMOTE_HIGHL,
};
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct i2c_client *client = to_i2c_client(dev);
struct lm90_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
int nr = attr->index;
down(&data->update_lock);
if (data->kind == adt7461)
data->temp11[nr] = TEMP2_TO_REG_ADT7461(val);
else
data->temp11[nr] = TEMP2_TO_REG(val);
i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2],
data->temp11[nr] >> 8);
i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1],
data->temp11[nr] & 0xff);
up(&data->update_lock);
return count;
}
static ssize_t show_temphyst(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm90_data *data = lm90_update_device(dev);
return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp8[attr->index])
- TEMP1_FROM_REG(data->temp_hyst));
}
static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm90_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
long hyst;
down(&data->update_lock);
hyst = TEMP1_FROM_REG(data->temp8[3]) - val;
i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST,
HYST_TO_REG(hyst));
up(&data->update_lock);
return count;
}
static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy,
char *buf)
{
struct lm90_data *data = lm90_update_device(dev);
return sprintf(buf, "%d\n", data->alarms);
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp8, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8,
set_temp8, 1);
static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11,
set_temp11, 1);
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8,
set_temp8, 2);
static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11,
set_temp11, 2);
static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8,
set_temp8, 3);
static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8,
set_temp8, 4);
static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst,
set_temphyst, 3);
static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 4);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
/*
* Real code
*/
static int lm90_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, lm90_detect);
}
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
*/
static int lm90_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct lm90_data *data;
int err = 0;
const char *name = "";
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct lm90_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct lm90_data));
/* The common I2C client data is placed right before the
LM90-specific data. */
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm90_driver;
new_client->flags = 0;
/*
* Now we do the remaining detection. A negative kind means that
* the driver was loaded with no force parameter (default), so we
* must both detect and identify the chip. A zero kind means that
* the driver was loaded with the force parameter, the detection
* step shall be skipped. A positive kind means that the driver
* was loaded with the force parameter and a given kind of chip is
* requested, so both the detection and the identification steps
* are skipped.
*/
/* Default to an LM90 if forced */
if (kind == 0)
kind = lm90;
if (kind < 0) { /* detection and identification */
u8 man_id, chip_id, reg_config1, reg_convrate;
man_id = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_MAN_ID);
chip_id = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_CHIP_ID);
reg_config1 = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_CONFIG1);
reg_convrate = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_CONVRATE);
if (man_id == 0x01) { /* National Semiconductor */
u8 reg_config2;
reg_config2 = i2c_smbus_read_byte_data(new_client,
LM90_REG_R_CONFIG2);
if ((reg_config1 & 0x2A) == 0x00
&& (reg_config2 & 0xF8) == 0x00
&& reg_convrate <= 0x09) {
if (address == 0x4C
&& (chip_id & 0xF0) == 0x20) { /* LM90 */
kind = lm90;
} else
if ((chip_id & 0xF0) == 0x30) { /* LM89/LM99 */
kind = lm99;
} else
if (address == 0x4C
&& (chip_id & 0xF0) == 0x10) { /* LM86 */
kind = lm86;
}
}
} else
if (man_id == 0x41) { /* Analog Devices */
if (address == 0x4C
&& (chip_id & 0xF0) == 0x40 /* ADM1032 */
&& (reg_config1 & 0x3F) == 0x00
&& reg_convrate <= 0x0A) {
kind = adm1032;
} else
if (address == 0x4c
&& chip_id == 0x51 /* ADT7461 */
&& (reg_config1 & 0x1F) == 0x00 /* check compat mode */
&& reg_convrate <= 0x0A) {
kind = adt7461;
}
} else
if (man_id == 0x4D) { /* Maxim */
/*
* The Maxim variants do NOT have a chip_id register.
* Reading from that address will return the last read
* value, which in our case is those of the man_id
* register. Likewise, the config1 register seems to
* lack a low nibble, so the value will be those of the
* previous read, so in our case those of the man_id
* register.
*/
if (chip_id == man_id
&& (reg_config1 & 0x1F) == (man_id & 0x0F)
&& reg_convrate <= 0x09) {
kind = max6657;
}
}
if (kind <= 0) { /* identification failed */
dev_info(&adapter->dev,
"Unsupported chip (man_id=0x%02X, "
"chip_id=0x%02X).\n", man_id, chip_id);
goto exit_free;
}
}
if (kind == lm90) {
name = "lm90";
} else if (kind == adm1032) {
name = "adm1032";
} else if (kind == lm99) {
name = "lm99";
} else if (kind == lm86) {
name = "lm86";
} else if (kind == max6657) {
name = "max6657";
} else if (kind == adt7461) {
name = "adt7461";
}
/* We can fill in the remaining client fields */
strlcpy(new_client->name, name, I2C_NAME_SIZE);
data->valid = 0;
data->kind = kind;
init_MUTEX(&data->update_lock);
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto exit_free;
/* Initialize the LM90 chip */
lm90_init_client(new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev,
&sensor_dev_attr_temp1_input.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp2_input.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp1_min.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp2_min.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp1_max.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp2_max.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp1_crit.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp2_crit.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp1_crit_hyst.dev_attr);
device_create_file(&new_client->dev,
&sensor_dev_attr_temp2_crit_hyst.dev_attr);
device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static void lm90_init_client(struct i2c_client *client)
{
u8 config;
/*
* Start the conversions.
*/
i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE,
5); /* 2 Hz */
config = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG1);
if (config & 0x40)
i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1,
config & 0xBF); /* run */
}
static int lm90_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;
}
kfree(i2c_get_clientdata(client));
return 0;
}
static struct lm90_data *lm90_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm90_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
u8 oldh, newh;
dev_dbg(&client->dev, "Updating lm90 data.\n");
data->temp8[0] = i2c_smbus_read_byte_data(client,
LM90_REG_R_LOCAL_TEMP);
data->temp8[1] = i2c_smbus_read_byte_data(client,
LM90_REG_R_LOCAL_LOW);
data->temp8[2] = i2c_smbus_read_byte_data(client,
LM90_REG_R_LOCAL_HIGH);
data->temp8[3] = i2c_smbus_read_byte_data(client,
LM90_REG_R_LOCAL_CRIT);
data->temp8[4] = i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_CRIT);
data->temp_hyst = i2c_smbus_read_byte_data(client,
LM90_REG_R_TCRIT_HYST);
/*
* There is a trick here. We have to read two registers to
* have the remote sensor temperature, but we have to beware
* a conversion could occur inbetween the readings. The
* datasheet says we should either use the one-shot
* conversion register, which we don't want to do (disables
* hardware monitoring) or monitor the busy bit, which is
* impossible (we can't read the values and monitor that bit
* at the exact same time). So the solution used here is to
* read the high byte once, then the low byte, then the high
* byte again. If the new high byte matches the old one,
* then we have a valid reading. Else we have to read the low
* byte again, and now we believe we have a correct reading.
*/
oldh = i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_TEMPH);
data->temp11[0] = i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_TEMPL);
newh = i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_TEMPH);
if (newh != oldh) {
data->temp11[0] = i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_TEMPL);
#ifdef DEBUG
oldh = i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_TEMPH);
/* oldh is actually newer */
if (newh != oldh)
dev_warn(&client->dev, "Remote temperature may be "
"wrong.\n");
#endif
}
data->temp11[0] |= (newh << 8);
data->temp11[1] = (i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_LOWH) << 8) +
i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_LOWL);
data->temp11[2] = (i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_HIGHH) << 8) +
i2c_smbus_read_byte_data(client,
LM90_REG_R_REMOTE_HIGHL);
data->alarms = i2c_smbus_read_byte_data(client,
LM90_REG_R_STATUS);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_lm90_init(void)
{
return i2c_add_driver(&lm90_driver);
}
static void __exit sensors_lm90_exit(void)
{
i2c_del_driver(&lm90_driver);
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("LM90/ADM1032 driver");
MODULE_LICENSE("GPL");
module_init(sensors_lm90_init);
module_exit(sensors_lm90_exit);

429
drivers/hwmon/lm92.c Normal file
Näytä tiedosto

@@ -0,0 +1,429 @@
/*
* lm92 - Hardware monitoring driver
* Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
*
* Based on the lm90 driver, with some ideas taken from the lm_sensors
* lm92 driver as well.
*
* The LM92 is a sensor chip made by National Semiconductor. It reports
* its own temperature with a 0.0625 deg resolution and a 0.33 deg
* accuracy. Complete datasheet can be obtained from National's website
* at:
* http://www.national.com/pf/LM/LM92.html
*
* This driver also supports the MAX6635 sensor chip made by Maxim.
* This chip is compatible with the LM92, but has a lesser accuracy
* (1.0 deg). Complete datasheet can be obtained from Maxim's website
* at:
* http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3074
*
* Since the LM92 was the first chipset supported by this driver, most
* comments will refer to this chipset, but are actually general and
* concern all supported chipsets, unless mentioned otherwise.
*
* Support could easily be added for the National Semiconductor LM76
* and Maxim MAX6633 and MAX6634 chips, which are mostly compatible
* with the LM92.
*
* 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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* The LM92 and MAX6635 have 2 two-state pins for address selection,
resulting in 4 possible addresses. */
static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(lm92);
/* The LM92 registers */
#define LM92_REG_CONFIG 0x01 /* 8-bit, RW */
#define LM92_REG_TEMP 0x00 /* 16-bit, RO */
#define LM92_REG_TEMP_HYST 0x02 /* 16-bit, RW */
#define LM92_REG_TEMP_CRIT 0x03 /* 16-bit, RW */
#define LM92_REG_TEMP_LOW 0x04 /* 16-bit, RW */
#define LM92_REG_TEMP_HIGH 0x05 /* 16-bit, RW */
#define LM92_REG_MAN_ID 0x07 /* 16-bit, RO, LM92 only */
/* The LM92 uses signed 13-bit values with LSB = 0.0625 degree Celsius,
left-justified in 16-bit registers. No rounding is done, with such
a resolution it's just not worth it. Note that the MAX6635 doesn't
make use of the 4 lower bits for limits (i.e. effective resolution
for limits is 1 degree Celsius). */
static inline int TEMP_FROM_REG(s16 reg)
{
return reg / 8 * 625 / 10;
}
static inline s16 TEMP_TO_REG(int val)
{
if (val <= -60000)
return -60000 * 10 / 625 * 8;
if (val >= 160000)
return 160000 * 10 / 625 * 8;
return val * 10 / 625 * 8;
}
/* Alarm flags are stored in the 3 LSB of the temperature register */
static inline u8 ALARMS_FROM_REG(s16 reg)
{
return reg & 0x0007;
}
/* Driver data (common to all clients) */
static struct i2c_driver lm92_driver;
/* Client data (each client gets its own) */
struct lm92_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
/* registers values */
s16 temp1_input, temp1_crit, temp1_min, temp1_max, temp1_hyst;
};
/*
* Sysfs attributes and callback functions
*/
static struct lm92_data *lm92_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm92_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ)
|| !data->valid) {
dev_dbg(&client->dev, "Updating lm92 data\n");
data->temp1_input = swab16(i2c_smbus_read_word_data(client,
LM92_REG_TEMP));
data->temp1_hyst = swab16(i2c_smbus_read_word_data(client,
LM92_REG_TEMP_HYST));
data->temp1_crit = swab16(i2c_smbus_read_word_data(client,
LM92_REG_TEMP_CRIT));
data->temp1_min = swab16(i2c_smbus_read_word_data(client,
LM92_REG_TEMP_LOW));
data->temp1_max = swab16(i2c_smbus_read_word_data(client,
LM92_REG_TEMP_HIGH));
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
#define show_temp(value) \
static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct lm92_data *data = lm92_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \
}
show_temp(temp1_input);
show_temp(temp1_crit);
show_temp(temp1_min);
show_temp(temp1_max);
#define set_temp(value, reg) \
static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct lm92_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock); \
data->value = TEMP_TO_REG(val); \
i2c_smbus_write_word_data(client, reg, swab16(data->value)); \
up(&data->update_lock); \
return count; \
}
set_temp(temp1_crit, LM92_REG_TEMP_CRIT);
set_temp(temp1_min, LM92_REG_TEMP_LOW);
set_temp(temp1_max, LM92_REG_TEMP_HIGH);
static ssize_t show_temp1_crit_hyst(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm92_data *data = lm92_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_crit)
- TEMP_FROM_REG(data->temp1_hyst));
}
static ssize_t show_temp1_max_hyst(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm92_data *data = lm92_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_max)
- TEMP_FROM_REG(data->temp1_hyst));
}
static ssize_t show_temp1_min_hyst(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm92_data *data = lm92_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_min)
+ TEMP_FROM_REG(data->temp1_hyst));
}
static ssize_t set_temp1_crit_hyst(struct device *dev, struct device_attribute *attr, const char *buf,
size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct lm92_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp1_hyst = TEMP_FROM_REG(data->temp1_crit) - val;
i2c_smbus_write_word_data(client, LM92_REG_TEMP_HYST,
swab16(TEMP_TO_REG(data->temp1_hyst)));
up(&data->update_lock);
return count;
}
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
{
struct lm92_data *data = lm92_update_device(dev);
return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->temp1_input));
}
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp1_input, NULL);
static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp1_crit,
set_temp1_crit);
static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp1_crit_hyst,
set_temp1_crit_hyst);
static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp1_min,
set_temp1_min);
static DEVICE_ATTR(temp1_min_hyst, S_IRUGO, show_temp1_min_hyst, NULL);
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp1_max,
set_temp1_max);
static DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp1_max_hyst, NULL);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
/*
* Detection and registration
*/
static void lm92_init_client(struct i2c_client *client)
{
u8 config;
/* Start the conversions if needed */
config = i2c_smbus_read_byte_data(client, LM92_REG_CONFIG);
if (config & 0x01)
i2c_smbus_write_byte_data(client, LM92_REG_CONFIG,
config & 0xFE);
}
/* The MAX6635 has no identification register, so we have to use tricks
to identify it reliably. This is somewhat slow.
Note that we do NOT rely on the 2 MSB of the configuration register
always reading 0, as suggested by the datasheet, because it was once
reported not to be true. */
static int max6635_check(struct i2c_client *client)
{
u16 temp_low, temp_high, temp_hyst, temp_crit;
u8 conf;
int i;
/* No manufacturer ID register, so a read from this address will
always return the last read value. */
temp_low = i2c_smbus_read_word_data(client, LM92_REG_TEMP_LOW);
if (i2c_smbus_read_word_data(client, LM92_REG_MAN_ID) != temp_low)
return 0;
temp_high = i2c_smbus_read_word_data(client, LM92_REG_TEMP_HIGH);
if (i2c_smbus_read_word_data(client, LM92_REG_MAN_ID) != temp_high)
return 0;
/* Limits are stored as integer values (signed, 9-bit). */
if ((temp_low & 0x7f00) || (temp_high & 0x7f00))
return 0;
temp_hyst = i2c_smbus_read_word_data(client, LM92_REG_TEMP_HYST);
temp_crit = i2c_smbus_read_word_data(client, LM92_REG_TEMP_CRIT);
if ((temp_hyst & 0x7f00) || (temp_crit & 0x7f00))
return 0;
/* Registers addresses were found to cycle over 16-byte boundaries.
We don't test all registers with all offsets so as to save some
reads and time, but this should still be sufficient to dismiss
non-MAX6635 chips. */
conf = i2c_smbus_read_byte_data(client, LM92_REG_CONFIG);
for (i=16; i<96; i*=2) {
if (temp_hyst != i2c_smbus_read_word_data(client,
LM92_REG_TEMP_HYST + i - 16)
|| temp_crit != i2c_smbus_read_word_data(client,
LM92_REG_TEMP_CRIT + i)
|| temp_low != i2c_smbus_read_word_data(client,
LM92_REG_TEMP_LOW + i + 16)
|| temp_high != i2c_smbus_read_word_data(client,
LM92_REG_TEMP_HIGH + i + 32)
|| conf != i2c_smbus_read_byte_data(client,
LM92_REG_CONFIG + i))
return 0;
}
return 1;
}
/* The following function does more than just detection. If detection
succeeds, it also registers the new chip. */
static int lm92_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct lm92_data *data;
int err = 0;
char *name;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
| I2C_FUNC_SMBUS_WORD_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct lm92_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct lm92_data));
/* Fill in enough client fields so that we can read from the chip,
which is required for identication */
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &lm92_driver;
new_client->flags = 0;
/* A negative kind means that the driver was loaded with no force
parameter (default), so we must identify the chip. */
if (kind < 0) {
u8 config = i2c_smbus_read_byte_data(new_client,
LM92_REG_CONFIG);
u16 man_id = i2c_smbus_read_word_data(new_client,
LM92_REG_MAN_ID);
if ((config & 0xe0) == 0x00
&& man_id == 0x0180) {
pr_info("lm92: Found National Semiconductor LM92 chip\n");
kind = lm92;
} else
if (max6635_check(new_client)) {
pr_info("lm92: Found Maxim MAX6635 chip\n");
kind = lm92; /* No separate prefix */
}
else
goto exit_free;
} else
if (kind == 0) /* Default to an LM92 if forced */
kind = lm92;
/* Give it the proper name */
if (kind == lm92) {
name = "lm92";
} else { /* Supposedly cannot happen */
dev_dbg(&new_client->dev, "Kind out of range?\n");
goto exit_free;
}
/* Fill in the remaining client fields */
strlcpy(new_client->name, name, I2C_NAME_SIZE);
data->valid = 0;
init_MUTEX(&data->update_lock);
/* Tell the i2c subsystem a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto exit_free;
/* Initialize the chipset */
lm92_init_client(new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_crit);
device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst);
device_create_file(&new_client->dev, &dev_attr_temp1_min);
device_create_file(&new_client->dev, &dev_attr_temp1_min_hyst);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static int lm92_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, lm92_detect);
}
static int lm92_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;
}
kfree(i2c_get_clientdata(client));
return 0;
}
/*
* Module and driver stuff
*/
static struct i2c_driver lm92_driver = {
.owner = THIS_MODULE,
.name = "lm92",
.id = I2C_DRIVERID_LM92,
.flags = I2C_DF_NOTIFY,
.attach_adapter = lm92_attach_adapter,
.detach_client = lm92_detach_client,
};
static int __init sensors_lm92_init(void)
{
return i2c_add_driver(&lm92_driver);
}
static void __exit sensors_lm92_exit(void)
{
i2c_del_driver(&lm92_driver);
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("LM92/MAX6635 driver");
MODULE_LICENSE("GPL");
module_init(sensors_lm92_init);
module_exit(sensors_lm92_exit);

372
drivers/hwmon/max1619.c Normal file
Näytä tiedosto

@@ -0,0 +1,372 @@
/*
* max1619.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
* Copyright (C) 2003-2004 Alexey Fisher <fishor@mail.ru>
* Jean Delvare <khali@linux-fr.org>
*
* Based on the lm90 driver. The MAX1619 is a sensor chip made by Maxim.
* It reports up to two temperatures (its own plus up to
* one external one). Complete datasheet can be
* obtained from Maxim's website at:
* http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf
*
* 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.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a,
0x29, 0x2a, 0x2b,
0x4c, 0x4d, 0x4e,
I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_1(max1619);
/*
* The MAX1619 registers
*/
#define MAX1619_REG_R_MAN_ID 0xFE
#define MAX1619_REG_R_CHIP_ID 0xFF
#define MAX1619_REG_R_CONFIG 0x03
#define MAX1619_REG_W_CONFIG 0x09
#define MAX1619_REG_R_CONVRATE 0x04
#define MAX1619_REG_W_CONVRATE 0x0A
#define MAX1619_REG_R_STATUS 0x02
#define MAX1619_REG_R_LOCAL_TEMP 0x00
#define MAX1619_REG_R_REMOTE_TEMP 0x01
#define MAX1619_REG_R_REMOTE_HIGH 0x07
#define MAX1619_REG_W_REMOTE_HIGH 0x0D
#define MAX1619_REG_R_REMOTE_LOW 0x08
#define MAX1619_REG_W_REMOTE_LOW 0x0E
#define MAX1619_REG_R_REMOTE_CRIT 0x10
#define MAX1619_REG_W_REMOTE_CRIT 0x12
#define MAX1619_REG_R_TCRIT_HYST 0x11
#define MAX1619_REG_W_TCRIT_HYST 0x13
/*
* Conversions and various macros
*/
#define TEMP_FROM_REG(val) ((val & 0x80 ? val-0x100 : val) * 1000)
#define TEMP_TO_REG(val) ((val < 0 ? val+0x100*1000 : val) / 1000)
/*
* Functions declaration
*/
static int max1619_attach_adapter(struct i2c_adapter *adapter);
static int max1619_detect(struct i2c_adapter *adapter, int address,
int kind);
static void max1619_init_client(struct i2c_client *client);
static int max1619_detach_client(struct i2c_client *client);
static struct max1619_data *max1619_update_device(struct device *dev);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver max1619_driver = {
.owner = THIS_MODULE,
.name = "max1619",
.flags = I2C_DF_NOTIFY,
.attach_adapter = max1619_attach_adapter,
.detach_client = max1619_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct max1619_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
/* registers values */
u8 temp_input1; /* local */
u8 temp_input2, temp_low2, temp_high2; /* remote */
u8 temp_crit2;
u8 temp_hyst2;
u8 alarms;
};
/*
* Sysfs stuff
*/
#define show_temp(value) \
static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct max1619_data *data = max1619_update_device(dev); \
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \
}
show_temp(temp_input1);
show_temp(temp_input2);
show_temp(temp_low2);
show_temp(temp_high2);
show_temp(temp_crit2);
show_temp(temp_hyst2);
#define set_temp2(value, reg) \
static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, \
size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct max1619_data *data = i2c_get_clientdata(client); \
long val = simple_strtol(buf, NULL, 10); \
\
down(&data->update_lock); \
data->value = TEMP_TO_REG(val); \
i2c_smbus_write_byte_data(client, reg, data->value); \
up(&data->update_lock); \
return count; \
}
set_temp2(temp_low2, MAX1619_REG_W_REMOTE_LOW);
set_temp2(temp_high2, MAX1619_REG_W_REMOTE_HIGH);
set_temp2(temp_crit2, MAX1619_REG_W_REMOTE_CRIT);
set_temp2(temp_hyst2, MAX1619_REG_W_TCRIT_HYST);
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
{
struct max1619_data *data = max1619_update_device(dev);
return sprintf(buf, "%d\n", data->alarms);
}
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL);
static DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input2, NULL);
static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_low2,
set_temp_low2);
static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_high2,
set_temp_high2);
static DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp_crit2,
set_temp_crit2);
static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp_hyst2,
set_temp_hyst2);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
/*
* Real code
*/
static int max1619_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, max1619_detect);
}
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
*/
static int max1619_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct max1619_data *data;
int err = 0;
const char *name = "";
u8 reg_config=0, reg_convrate=0, reg_status=0;
u8 man_id, chip_id;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct max1619_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct max1619_data));
/* The common I2C client data is placed right before the
MAX1619-specific data. */
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &max1619_driver;
new_client->flags = 0;
/*
* Now we do the remaining detection. A negative kind means that
* the driver was loaded with no force parameter (default), so we
* must both detect and identify the chip. A zero kind means that
* the driver was loaded with the force parameter, the detection
* step shall be skipped. A positive kind means that the driver
* was loaded with the force parameter and a given kind of chip is
* requested, so both the detection and the identification steps
* are skipped.
*/
if (kind < 0) { /* detection */
reg_config = i2c_smbus_read_byte_data(new_client,
MAX1619_REG_R_CONFIG);
reg_convrate = i2c_smbus_read_byte_data(new_client,
MAX1619_REG_R_CONVRATE);
reg_status = i2c_smbus_read_byte_data(new_client,
MAX1619_REG_R_STATUS);
if ((reg_config & 0x03) != 0x00
|| reg_convrate > 0x07 || (reg_status & 0x61 ) !=0x00) {
dev_dbg(&adapter->dev,
"MAX1619 detection failed at 0x%02x.\n",
address);
goto exit_free;
}
}
if (kind <= 0) { /* identification */
man_id = i2c_smbus_read_byte_data(new_client,
MAX1619_REG_R_MAN_ID);
chip_id = i2c_smbus_read_byte_data(new_client,
MAX1619_REG_R_CHIP_ID);
if ((man_id == 0x4D) && (chip_id == 0x04)){
kind = max1619;
}
}
if (kind <= 0) { /* identification failed */
dev_info(&adapter->dev,
"Unsupported chip (man_id=0x%02X, "
"chip_id=0x%02X).\n", man_id, chip_id);
goto exit_free;
}
if (kind == max1619){
name = "max1619";
}
/* We can fill in the remaining client fields */
strlcpy(new_client->name, name, I2C_NAME_SIZE);
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 exit_free;
/* Initialize the MAX1619 chip */
max1619_init_client(new_client);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp2_input);
device_create_file(&new_client->dev, &dev_attr_temp2_min);
device_create_file(&new_client->dev, &dev_attr_temp2_max);
device_create_file(&new_client->dev, &dev_attr_temp2_crit);
device_create_file(&new_client->dev, &dev_attr_temp2_crit_hyst);
device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static void max1619_init_client(struct i2c_client *client)
{
u8 config;
/*
* Start the conversions.
*/
i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONVRATE,
5); /* 2 Hz */
config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG);
if (config & 0x40)
i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONFIG,
config & 0xBF); /* run */
}
static int max1619_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;
}
kfree(i2c_get_clientdata(client));
return 0;
}
static struct max1619_data *max1619_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct max1619_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
dev_dbg(&client->dev, "Updating max1619 data.\n");
data->temp_input1 = i2c_smbus_read_byte_data(client,
MAX1619_REG_R_LOCAL_TEMP);
data->temp_input2 = i2c_smbus_read_byte_data(client,
MAX1619_REG_R_REMOTE_TEMP);
data->temp_high2 = i2c_smbus_read_byte_data(client,
MAX1619_REG_R_REMOTE_HIGH);
data->temp_low2 = i2c_smbus_read_byte_data(client,
MAX1619_REG_R_REMOTE_LOW);
data->temp_crit2 = i2c_smbus_read_byte_data(client,
MAX1619_REG_R_REMOTE_CRIT);
data->temp_hyst2 = i2c_smbus_read_byte_data(client,
MAX1619_REG_R_TCRIT_HYST);
data->alarms = i2c_smbus_read_byte_data(client,
MAX1619_REG_R_STATUS);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_max1619_init(void)
{
return i2c_add_driver(&max1619_driver);
}
static void __exit sensors_max1619_exit(void)
{
i2c_del_driver(&max1619_driver);
}
MODULE_AUTHOR("Alexey Fisher <fishor@mail.ru> and"
"Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("MAX1619 sensor driver");
MODULE_LICENSE("GPL");
module_init(sensors_max1619_init);
module_exit(sensors_max1619_exit);

1348
drivers/hwmon/pc87360.c Normal file

File diff suppressed because it is too large Load Diff

817
drivers/hwmon/sis5595.c Normal file
Näytä tiedosto

@@ -0,0 +1,817 @@
/*
sis5595.c - Part of lm_sensors, Linux kernel modules
for hardware monitoring
Copyright (C) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>,
Ky<4B>sti M<>lkki <kmalkki@cc.hut.fi>, and
Mark D. Studebaker <mdsxyz123@yahoo.com>
Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with
the help of Jean Delvare <khali@linux-fr.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.
*/
/*
SiS southbridge has a LM78-like chip integrated on the same IC.
This driver is a customized copy of lm78.c
Supports following revisions:
Version PCI ID PCI Revision
1 1039/0008 AF or less
2 1039/0008 B0 or greater
Note: these chips contain a 0008 device which is incompatible with the
5595. We recognize these by the presence of the listed
"blacklist" PCI ID and refuse to load.
NOT SUPPORTED PCI ID BLACKLIST PCI ID
540 0008 0540
550 0008 0550
5513 0008 5511
5581 0008 5597
5582 0008 5597
5597 0008 5597
5598 0008 5597/5598
630 0008 0630
645 0008 0645
730 0008 0730
735 0008 0735
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/init.h>
#include <linux/jiffies.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 u16 force_addr;
module_param(force_addr, ushort, 0);
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[] = { I2C_CLIENT_END };
static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(sis5595);
/* Many SIS5595 constants specified below */
/* Length of ISA address segment */
#define SIS5595_EXTENT 8
/* PCI Config Registers */
#define SIS5595_REVISION_REG 0x08
#define SIS5595_BASE_REG 0x68
#define SIS5595_PIN_REG 0x7A
#define SIS5595_ENABLE_REG 0x7B
/* Where are the ISA address/data registers relative to the base address */
#define SIS5595_ADDR_REG_OFFSET 5
#define SIS5595_DATA_REG_OFFSET 6
/* The SIS5595 registers */
#define SIS5595_REG_IN_MAX(nr) (0x2b + (nr) * 2)
#define SIS5595_REG_IN_MIN(nr) (0x2c + (nr) * 2)
#define SIS5595_REG_IN(nr) (0x20 + (nr))
#define SIS5595_REG_FAN_MIN(nr) (0x3b + (nr))
#define SIS5595_REG_FAN(nr) (0x28 + (nr))
/* On the first version of the chip, the temp registers are separate.
On the second version,
TEMP pin is shared with IN4, configured in PCI register 0x7A.
The registers are the same as well.
OVER and HYST are really MAX and MIN. */
#define REV2MIN 0xb0
#define SIS5595_REG_TEMP (( data->revision) >= REV2MIN) ? \
SIS5595_REG_IN(4) : 0x27
#define SIS5595_REG_TEMP_OVER (( data->revision) >= REV2MIN) ? \
SIS5595_REG_IN_MAX(4) : 0x39
#define SIS5595_REG_TEMP_HYST (( data->revision) >= REV2MIN) ? \
SIS5595_REG_IN_MIN(4) : 0x3a
#define SIS5595_REG_CONFIG 0x40
#define SIS5595_REG_ALARM1 0x41
#define SIS5595_REG_ALARM2 0x42
#define SIS5595_REG_FANDIV 0x47
/* Conversions. Limit checking is only done on the TO_REG
variants. */
/* IN: mV, (0V to 4.08V)
REG: 16mV/bit */
static inline u8 IN_TO_REG(unsigned long val)
{
unsigned long nval = SENSORS_LIMIT(val, 0, 4080);
return (nval + 8) / 16;
}
#define IN_FROM_REG(val) ((val) * 16)
static inline u8 FAN_TO_REG(long rpm, int div)
{
if (rpm <= 0)
return 255;
return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
}
static inline int FAN_FROM_REG(u8 val, int div)
{
return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div);
}
/* TEMP: mC (-54.12C to +157.53C)
REG: 0.83C/bit + 52.12, two's complement */
static inline int TEMP_FROM_REG(s8 val)
{
return val * 830 + 52120;
}
static inline s8 TEMP_TO_REG(int val)
{
int nval = SENSORS_LIMIT(val, -54120, 157530) ;
return nval<0 ? (nval-5212-415)/830 : (nval-5212+415)/830;
}
/* FAN DIV: 1, 2, 4, or 8 (defaults to 2)
REG: 0, 1, 2, or 3 (respectively) (defaults to 1) */
static inline u8 DIV_TO_REG(int val)
{
return val==8 ? 3 : val==4 ? 2 : val==1 ? 0 : 1;
}
#define DIV_FROM_REG(val) (1 << (val))
/* For the SIS5595, we need to keep some data in memory. That
data is pointed to by sis5595_list[NR]->data. The structure itself is
dynamically allocated, at the time when the new sis5595 client is
allocated. */
struct sis5595_data {
struct i2c_client client;
struct semaphore lock;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
char maxins; /* == 3 if temp enabled, otherwise == 4 */
u8 revision; /* Reg. value */
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 */
s8 temp; /* Register value */
s8 temp_over; /* Register value */
s8 temp_hyst; /* 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) sis5595 */
static int sis5595_attach_adapter(struct i2c_adapter *adapter);
static int sis5595_detect(struct i2c_adapter *adapter, int address, int kind);
static int sis5595_detach_client(struct i2c_client *client);
static int sis5595_read_value(struct i2c_client *client, u8 register);
static int sis5595_write_value(struct i2c_client *client, u8 register, u8 value);
static struct sis5595_data *sis5595_update_device(struct device *dev);
static void sis5595_init_client(struct i2c_client *client);
static struct i2c_driver sis5595_driver = {
.owner = THIS_MODULE,
.name = "sis5595",
.id = I2C_DRIVERID_SIS5595,
.flags = I2C_DF_NOTIFY,
.attach_adapter = sis5595_attach_adapter,
.detach_client = sis5595_detach_client,
};
/* 4 Voltages */
static ssize_t show_in(struct device *dev, char *buf, int nr)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr]));
}
static ssize_t show_in_min(struct device *dev, char *buf, int nr)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr]));
}
static ssize_t show_in_max(struct device *dev, char *buf, int nr)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr]));
}
static ssize_t set_in_min(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct sis5595_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->in_min[nr] = IN_TO_REG(val);
sis5595_write_value(client, SIS5595_REG_IN_MIN(nr), data->in_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t set_in_max(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct sis5595_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->in_max[nr] = IN_TO_REG(val);
sis5595_write_value(client, SIS5595_REG_IN_MAX(nr), data->in_max[nr]);
up(&data->update_lock);
return count;
}
#define show_in_offset(offset) \
static ssize_t \
show_in##offset (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_in(dev, buf, offset); \
} \
static DEVICE_ATTR(in##offset##_input, S_IRUGO, \
show_in##offset, NULL); \
static ssize_t \
show_in##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_in_min(dev, buf, offset); \
} \
static ssize_t \
show_in##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_in_max(dev, buf, offset); \
} \
static ssize_t set_in##offset##_min (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_in_min(dev, buf, count, offset); \
} \
static ssize_t set_in##offset##_max (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_in_max(dev, buf, count, offset); \
} \
static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
show_in##offset##_min, set_in##offset##_min); \
static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
show_in##offset##_max, set_in##offset##_max);
show_in_offset(0);
show_in_offset(1);
show_in_offset(2);
show_in_offset(3);
show_in_offset(4);
/* Temperature */
static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp));
}
static ssize_t show_temp_over(struct device *dev, struct device_attribute *attr, char *buf)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over));
}
static ssize_t set_temp_over(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct sis5595_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_over = TEMP_TO_REG(val);
sis5595_write_value(client, SIS5595_REG_TEMP_OVER, data->temp_over);
up(&data->update_lock);
return count;
}
static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *attr, char *buf)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst));
}
static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct sis5595_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_hyst = TEMP_TO_REG(val);
sis5595_write_value(client, SIS5595_REG_TEMP_HYST, data->temp_hyst);
up(&data->update_lock);
return count;
}
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
show_temp_over, set_temp_over);
static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
show_temp_hyst, set_temp_hyst);
/* 2 Fans */
static ssize_t show_fan(struct device *dev, char *buf, int nr)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
DIV_FROM_REG(data->fan_div[nr])) );
}
static ssize_t show_fan_min(struct device *dev, char *buf, int nr)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr],
DIV_FROM_REG(data->fan_div[nr])) );
}
static ssize_t set_fan_min(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct sis5595_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
sis5595_write_value(client, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) );
}
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan divisor. This follows the principle of
least suprise; the user doesn't expect the fan minimum to change just
because the divisor changed. */
static ssize_t set_fan_div(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct sis5595_data *data = i2c_get_clientdata(client);
unsigned long min;
unsigned long val = simple_strtoul(buf, NULL, 10);
int reg;
down(&data->update_lock);
min = FAN_FROM_REG(data->fan_min[nr],
DIV_FROM_REG(data->fan_div[nr]));
reg = sis5595_read_value(client, SIS5595_REG_FANDIV);
switch (val) {
case 1: data->fan_div[nr] = 0; break;
case 2: data->fan_div[nr] = 1; break;
case 4: data->fan_div[nr] = 2; break;
case 8: data->fan_div[nr] = 3; break;
default:
dev_err(&client->dev, "fan_div value %ld not "
"supported. Choose one of 1, 2, 4 or 8!\n", val);
up(&data->update_lock);
return -EINVAL;
}
switch (nr) {
case 0:
reg = (reg & 0xcf) | (data->fan_div[nr] << 4);
break;
case 1:
reg = (reg & 0x3f) | (data->fan_div[nr] << 6);
break;
}
sis5595_write_value(client, SIS5595_REG_FANDIV, reg);
data->fan_min[nr] =
FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
sis5595_write_value(client, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]);
up(&data->update_lock);
return count;
}
#define show_fan_offset(offset) \
static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_fan(dev, buf, offset - 1); \
} \
static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_fan_min(dev, buf, offset - 1); \
} \
static ssize_t show_fan_##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_fan_div(dev, buf, offset - 1); \
} \
static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_fan_min(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\
static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
show_fan_##offset##_min, set_fan_##offset##_min);
show_fan_offset(1);
show_fan_offset(2);
static ssize_t set_fan_1_div(struct device *dev, struct device_attribute *attr, const char *buf,
size_t count)
{
return set_fan_div(dev, buf, count, 0) ;
}
static ssize_t set_fan_2_div(struct device *dev, struct device_attribute *attr, const char *buf,
size_t count)
{
return set_fan_div(dev, buf, count, 1) ;
}
static DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR,
show_fan_1_div, set_fan_1_div);
static DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR,
show_fan_2_div, set_fan_2_div);
/* Alarms */
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
{
struct sis5595_data *data = sis5595_update_device(dev);
return sprintf(buf, "%d\n", data->alarms);
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
/* This is called when the module is loaded */
static int sis5595_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, sis5595_detect);
}
int sis5595_detect(struct i2c_adapter *adapter, int address, int kind)
{
int err = 0;
int i;
struct i2c_client *new_client;
struct sis5595_data *data;
char val;
u16 a;
/* Make sure we are probing the ISA bus!! */
if (!i2c_is_isa_adapter(adapter))
goto exit;
if (force_addr)
address = force_addr & ~(SIS5595_EXTENT - 1);
/* Reserve the ISA region */
if (!request_region(address, SIS5595_EXTENT, sis5595_driver.name)) {
err = -EBUSY;
goto exit;
}
if (force_addr) {
dev_warn(&adapter->dev, "forcing ISA address 0x%04X\n", address);
if (PCIBIOS_SUCCESSFUL !=
pci_write_config_word(s_bridge, SIS5595_BASE_REG, address))
goto exit_release;
if (PCIBIOS_SUCCESSFUL !=
pci_read_config_word(s_bridge, SIS5595_BASE_REG, &a))
goto exit_release;
if ((a & ~(SIS5595_EXTENT - 1)) != address)
/* doesn't work for some chips? */
goto exit_release;
}
if (PCIBIOS_SUCCESSFUL !=
pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val)) {
goto exit_release;
}
if ((val & 0x80) == 0) {
if (PCIBIOS_SUCCESSFUL !=
pci_write_config_byte(s_bridge, SIS5595_ENABLE_REG,
val | 0x80))
goto exit_release;
if (PCIBIOS_SUCCESSFUL !=
pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val))
goto exit_release;
if ((val & 0x80) == 0)
/* doesn't work for some chips! */
goto exit_release;
}
if (!(data = kmalloc(sizeof(struct sis5595_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit_release;
}
memset(data, 0, sizeof(struct sis5595_data));
new_client = &data->client;
new_client->addr = address;
init_MUTEX(&data->lock);
i2c_set_clientdata(new_client, data);
new_client->adapter = adapter;
new_client->driver = &sis5595_driver;
new_client->flags = 0;
/* Check revision and pin registers to determine whether 4 or 5 voltages */
pci_read_config_byte(s_bridge, SIS5595_REVISION_REG, &(data->revision));
/* 4 voltages, 1 temp */
data->maxins = 3;
if (data->revision >= REV2MIN) {
pci_read_config_byte(s_bridge, SIS5595_PIN_REG, &val);
if (!(val & 0x80))
/* 5 voltages, no temps */
data->maxins = 4;
}
/* Fill in the remaining client fields and put it into the global list */
strlcpy(new_client->name, "sis5595", I2C_NAME_SIZE);
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 exit_free;
/* Initialize the SIS5595 chip */
sis5595_init_client(new_client);
/* A few vars need to be filled upon startup */
for (i = 0; i < 2; i++) {
data->fan_min[i] = sis5595_read_value(new_client,
SIS5595_REG_FAN_MIN(i));
}
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_in0_input);
device_create_file(&new_client->dev, &dev_attr_in0_min);
device_create_file(&new_client->dev, &dev_attr_in0_max);
device_create_file(&new_client->dev, &dev_attr_in1_input);
device_create_file(&new_client->dev, &dev_attr_in1_min);
device_create_file(&new_client->dev, &dev_attr_in1_max);
device_create_file(&new_client->dev, &dev_attr_in2_input);
device_create_file(&new_client->dev, &dev_attr_in2_min);
device_create_file(&new_client->dev, &dev_attr_in2_max);
device_create_file(&new_client->dev, &dev_attr_in3_input);
device_create_file(&new_client->dev, &dev_attr_in3_min);
device_create_file(&new_client->dev, &dev_attr_in3_max);
if (data->maxins == 4) {
device_create_file(&new_client->dev, &dev_attr_in4_input);
device_create_file(&new_client->dev, &dev_attr_in4_min);
device_create_file(&new_client->dev, &dev_attr_in4_max);
}
device_create_file(&new_client->dev, &dev_attr_fan1_input);
device_create_file(&new_client->dev, &dev_attr_fan1_min);
device_create_file(&new_client->dev, &dev_attr_fan1_div);
device_create_file(&new_client->dev, &dev_attr_fan2_input);
device_create_file(&new_client->dev, &dev_attr_fan2_min);
device_create_file(&new_client->dev, &dev_attr_fan2_div);
device_create_file(&new_client->dev, &dev_attr_alarms);
if (data->maxins == 3) {
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
}
return 0;
exit_free:
kfree(data);
exit_release:
release_region(address, SIS5595_EXTENT);
exit:
return err;
}
static int sis5595_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;
}
if (i2c_is_isa_client(client))
release_region(client->addr, SIS5595_EXTENT);
kfree(i2c_get_clientdata(client));
return 0;
}
/* ISA access must be locked explicitly. */
static int sis5595_read_value(struct i2c_client *client, u8 reg)
{
int res;
struct sis5595_data *data = i2c_get_clientdata(client);
down(&data->lock);
outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET);
res = inb_p(client->addr + SIS5595_DATA_REG_OFFSET);
up(&data->lock);
return res;
}
static int sis5595_write_value(struct i2c_client *client, u8 reg, u8 value)
{
struct sis5595_data *data = i2c_get_clientdata(client);
down(&data->lock);
outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET);
outb_p(value, client->addr + SIS5595_DATA_REG_OFFSET);
up(&data->lock);
return 0;
}
/* Called when we have found a new SIS5595. */
static void sis5595_init_client(struct i2c_client *client)
{
u8 config = sis5595_read_value(client, SIS5595_REG_CONFIG);
if (!(config & 0x01))
sis5595_write_value(client, SIS5595_REG_CONFIG,
(config & 0xf7) | 0x01);
}
static struct sis5595_data *sis5595_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct sis5595_data *data = i2c_get_clientdata(client);
int i;
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
for (i = 0; i <= data->maxins; i++) {
data->in[i] =
sis5595_read_value(client, SIS5595_REG_IN(i));
data->in_min[i] =
sis5595_read_value(client,
SIS5595_REG_IN_MIN(i));
data->in_max[i] =
sis5595_read_value(client,
SIS5595_REG_IN_MAX(i));
}
for (i = 0; i < 2; i++) {
data->fan[i] =
sis5595_read_value(client, SIS5595_REG_FAN(i));
data->fan_min[i] =
sis5595_read_value(client,
SIS5595_REG_FAN_MIN(i));
}
if (data->maxins == 3) {
data->temp =
sis5595_read_value(client, SIS5595_REG_TEMP);
data->temp_over =
sis5595_read_value(client, SIS5595_REG_TEMP_OVER);
data->temp_hyst =
sis5595_read_value(client, SIS5595_REG_TEMP_HYST);
}
i = sis5595_read_value(client, SIS5595_REG_FANDIV);
data->fan_div[0] = (i >> 4) & 0x03;
data->fan_div[1] = i >> 6;
data->alarms =
sis5595_read_value(client, SIS5595_REG_ALARM1) |
(sis5595_read_value(client, SIS5595_REG_ALARM2) << 8);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static struct pci_device_id sis5595_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, sis5595_pci_ids);
static int blacklist[] __devinitdata = {
PCI_DEVICE_ID_SI_540,
PCI_DEVICE_ID_SI_550,
PCI_DEVICE_ID_SI_630,
PCI_DEVICE_ID_SI_645,
PCI_DEVICE_ID_SI_730,
PCI_DEVICE_ID_SI_735,
PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but
that ID shows up in other chips so we
use the 5511 ID for recognition */
PCI_DEVICE_ID_SI_5597,
PCI_DEVICE_ID_SI_5598,
0 };
static int __devinit sis5595_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
u16 val;
int *i;
int addr = 0;
for (i = blacklist; *i != 0; i++) {
struct pci_dev *dev;
dev = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL);
if (dev) {
dev_err(&dev->dev, "Looked for SIS5595 but found unsupported device %.4x\n", *i);
pci_dev_put(dev);
return -ENODEV;
}
}
if (PCIBIOS_SUCCESSFUL !=
pci_read_config_word(dev, SIS5595_BASE_REG, &val))
return -ENODEV;
addr = val & ~(SIS5595_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 SiS 5595 sensors found.\n");
return -ENODEV;
}
normal_isa[0] = addr;
s_bridge = pci_dev_get(dev);
if (i2c_add_driver(&sis5595_driver)) {
pci_dev_put(s_bridge);
s_bridge = NULL;
}
/* Always return failure here. This is to allow other drivers to bind
* to this pci device. We don't really want to have control over the
* pci device, we only wanted to read as few register values from it.
*/
return -ENODEV;
}
static struct pci_driver sis5595_pci_driver = {
.name = "sis5595",
.id_table = sis5595_pci_ids,
.probe = sis5595_pci_probe,
};
static int __init sm_sis5595_init(void)
{
return pci_register_driver(&sis5595_pci_driver);
}
static void __exit sm_sis5595_exit(void)
{
pci_unregister_driver(&sis5595_pci_driver);
if (s_bridge != NULL) {
i2c_del_driver(&sis5595_driver);
pci_dev_put(s_bridge);
s_bridge = NULL;
}
}
MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>");
MODULE_DESCRIPTION("SiS 5595 Sensor device");
MODULE_LICENSE("GPL");
module_init(sm_sis5595_init);
module_exit(sm_sis5595_exit);

352
drivers/hwmon/smsc47b397.c Normal file
Näytä tiedosto

@@ -0,0 +1,352 @@
/*
smsc47b397.c - Part of lm_sensors, Linux kernel modules
for hardware monitoring
Supports the SMSC LPC47B397-NC Super-I/O chip.
Author/Maintainer: Mark M. Hoffman <mhoffman@lightlink.com>
Copyright (C) 2004 Utilitek Systems, Inc.
derived in part from smsc47m1.c:
Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
Copyright (C) 2004 Jean Delvare <khali@linux-fr.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.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/init.h>
#include <asm/io.h>
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
/* Address is autodetected, there is no default value */
static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END };
static struct i2c_force_data forces[] = {{NULL}};
enum chips { any_chip, smsc47b397 };
static struct i2c_address_data addr_data = {
.normal_i2c = normal_i2c,
.normal_isa = normal_isa,
.probe = normal_i2c, /* cheat */
.ignore = normal_i2c, /* cheat */
.forces = forces,
};
/* Super-I/0 registers and commands */
#define REG 0x2e /* The register to read/write */
#define VAL 0x2f /* The value to read/write */
static inline void superio_outb(int reg, int val)
{
outb(reg, REG);
outb(val, VAL);
}
static inline int superio_inb(int reg)
{
outb(reg, REG);
return inb(VAL);
}
/* select superio logical device */
static inline void superio_select(int ld)
{
superio_outb(0x07, ld);
}
static inline void superio_enter(void)
{
outb(0x55, REG);
}
static inline void superio_exit(void)
{
outb(0xAA, REG);
}
#define SUPERIO_REG_DEVID 0x20
#define SUPERIO_REG_DEVREV 0x21
#define SUPERIO_REG_BASE_MSB 0x60
#define SUPERIO_REG_BASE_LSB 0x61
#define SUPERIO_REG_LD8 0x08
#define SMSC_EXTENT 0x02
/* 0 <= nr <= 3 */
static u8 smsc47b397_reg_temp[] = {0x25, 0x26, 0x27, 0x80};
#define SMSC47B397_REG_TEMP(nr) (smsc47b397_reg_temp[(nr)])
/* 0 <= nr <= 3 */
#define SMSC47B397_REG_FAN_LSB(nr) (0x28 + 2 * (nr))
#define SMSC47B397_REG_FAN_MSB(nr) (0x29 + 2 * (nr))
struct smsc47b397_data {
struct i2c_client client;
struct semaphore lock;
struct semaphore update_lock;
unsigned long last_updated; /* in jiffies */
int valid;
/* register values */
u16 fan[4];
u8 temp[4];
};
static int smsc47b397_read_value(struct i2c_client *client, u8 reg)
{
struct smsc47b397_data *data = i2c_get_clientdata(client);
int res;
down(&data->lock);
outb(reg, client->addr);
res = inb_p(client->addr + 1);
up(&data->lock);
return res;
}
static struct smsc47b397_data *smsc47b397_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct smsc47b397_data *data = i2c_get_clientdata(client);
int i;
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
dev_dbg(&client->dev, "starting device update...\n");
/* 4 temperature inputs, 4 fan inputs */
for (i = 0; i < 4; i++) {
data->temp[i] = smsc47b397_read_value(client,
SMSC47B397_REG_TEMP(i));
/* must read LSB first */
data->fan[i] = smsc47b397_read_value(client,
SMSC47B397_REG_FAN_LSB(i));
data->fan[i] |= smsc47b397_read_value(client,
SMSC47B397_REG_FAN_MSB(i)) << 8;
}
data->last_updated = jiffies;
data->valid = 1;
dev_dbg(&client->dev, "... device update complete\n");
}
up(&data->update_lock);
return data;
}
/* TEMP: 0.001C/bit (-128C to +127C)
REG: 1C/bit, two's complement */
static int temp_from_reg(u8 reg)
{
return (s8)reg * 1000;
}
/* 0 <= nr <= 3 */
static ssize_t show_temp(struct device *dev, char *buf, int nr)
{
struct smsc47b397_data *data = smsc47b397_update_device(dev);
return sprintf(buf, "%d\n", temp_from_reg(data->temp[nr]));
}
#define sysfs_temp(num) \
static ssize_t show_temp##num(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_temp(dev, buf, num-1); \
} \
static DEVICE_ATTR(temp##num##_input, S_IRUGO, show_temp##num, NULL)
sysfs_temp(1);
sysfs_temp(2);
sysfs_temp(3);
sysfs_temp(4);
#define device_create_file_temp(client, num) \
device_create_file(&client->dev, &dev_attr_temp##num##_input)
/* FAN: 1 RPM/bit
REG: count of 90kHz pulses / revolution */
static int fan_from_reg(u16 reg)
{
return 90000 * 60 / reg;
}
/* 0 <= nr <= 3 */
static ssize_t show_fan(struct device *dev, char *buf, int nr)
{
struct smsc47b397_data *data = smsc47b397_update_device(dev);
return sprintf(buf, "%d\n", fan_from_reg(data->fan[nr]));
}
#define sysfs_fan(num) \
static ssize_t show_fan##num(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_fan(dev, buf, num-1); \
} \
static DEVICE_ATTR(fan##num##_input, S_IRUGO, show_fan##num, NULL)
sysfs_fan(1);
sysfs_fan(2);
sysfs_fan(3);
sysfs_fan(4);
#define device_create_file_fan(client, num) \
device_create_file(&client->dev, &dev_attr_fan##num##_input)
static int smsc47b397_detect(struct i2c_adapter *adapter, int addr, int kind);
static int smsc47b397_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, smsc47b397_detect);
}
static int smsc47b397_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, SMSC_EXTENT);
kfree(i2c_get_clientdata(client));
return 0;
}
static struct i2c_driver smsc47b397_driver = {
.owner = THIS_MODULE,
.name = "smsc47b397",
.id = I2C_DRIVERID_SMSC47B397,
.flags = I2C_DF_NOTIFY,
.attach_adapter = smsc47b397_attach_adapter,
.detach_client = smsc47b397_detach_client,
};
static int smsc47b397_detect(struct i2c_adapter *adapter, int addr, int kind)
{
struct i2c_client *new_client;
struct smsc47b397_data *data;
int err = 0;
if (!i2c_is_isa_adapter(adapter)) {
return 0;
}
if (!request_region(addr, SMSC_EXTENT, smsc47b397_driver.name)) {
dev_err(&adapter->dev, "Region 0x%x already in use!\n", addr);
return -EBUSY;
}
if (!(data = kmalloc(sizeof(struct smsc47b397_data), GFP_KERNEL))) {
err = -ENOMEM;
goto error_release;
}
memset(data, 0x00, sizeof(struct smsc47b397_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = addr;
init_MUTEX(&data->lock);
new_client->adapter = adapter;
new_client->driver = &smsc47b397_driver;
new_client->flags = 0;
strlcpy(new_client->name, "smsc47b397", I2C_NAME_SIZE);
init_MUTEX(&data->update_lock);
if ((err = i2c_attach_client(new_client)))
goto error_free;
device_create_file_temp(new_client, 1);
device_create_file_temp(new_client, 2);
device_create_file_temp(new_client, 3);
device_create_file_temp(new_client, 4);
device_create_file_fan(new_client, 1);
device_create_file_fan(new_client, 2);
device_create_file_fan(new_client, 3);
device_create_file_fan(new_client, 4);
return 0;
error_free:
kfree(new_client);
error_release:
release_region(addr, SMSC_EXTENT);
return err;
}
static int __init smsc47b397_find(unsigned int *addr)
{
u8 id, rev;
superio_enter();
id = superio_inb(SUPERIO_REG_DEVID);
if (id != 0x6f) {
superio_exit();
return -ENODEV;
}
rev = superio_inb(SUPERIO_REG_DEVREV);
superio_select(SUPERIO_REG_LD8);
*addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8)
| superio_inb(SUPERIO_REG_BASE_LSB);
printk(KERN_INFO "smsc47b397: found SMSC LPC47B397-NC "
"(base address 0x%04x, revision %u)\n", *addr, rev);
superio_exit();
return 0;
}
static int __init smsc47b397_init(void)
{
int ret;
if ((ret = smsc47b397_find(normal_isa)))
return ret;
return i2c_add_driver(&smsc47b397_driver);
}
static void __exit smsc47b397_exit(void)
{
i2c_del_driver(&smsc47b397_driver);
}
MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
MODULE_DESCRIPTION("SMSC LPC47B397 driver");
MODULE_LICENSE("GPL");
module_init(smsc47b397_init);
module_exit(smsc47b397_exit);

593
drivers/hwmon/smsc47m1.c Normal file
Näytä tiedosto

@@ -0,0 +1,593 @@
/*
smsc47m1.c - Part of lm_sensors, Linux kernel modules
for hardware monitoring
Supports the SMSC LPC47B27x, LPC47M10x, LPC47M13x and LPC47M14x
Super-I/O chips.
Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
Copyright (C) 2004 Jean Delvare <khali@linux-fr.org>
Ported to Linux 2.6 by Gabriele Gorla <gorlik@yahoo.com>
and Jean Delvare
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.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <linux/init.h>
#include <asm/io.h>
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
/* Address is autodetected, there is no default value */
static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END };
static struct i2c_force_data forces[] = {{NULL}};
enum chips { any_chip, smsc47m1 };
static struct i2c_address_data addr_data = {
.normal_i2c = normal_i2c,
.normal_isa = normal_isa,
.forces = forces,
};
/* Super-I/0 registers and commands */
#define REG 0x2e /* The register to read/write */
#define VAL 0x2f /* The value to read/write */
static inline void
superio_outb(int reg, int val)
{
outb(reg, REG);
outb(val, VAL);
}
static inline int
superio_inb(int reg)
{
outb(reg, REG);
return inb(VAL);
}
/* logical device for fans is 0x0A */
#define superio_select() superio_outb(0x07, 0x0A)
static inline void
superio_enter(void)
{
outb(0x55, REG);
}
static inline void
superio_exit(void)
{
outb(0xAA, REG);
}
#define SUPERIO_REG_ACT 0x30
#define SUPERIO_REG_BASE 0x60
#define SUPERIO_REG_DEVID 0x20
/* Logical device registers */
#define SMSC_EXTENT 0x80
/* nr is 0 or 1 in the macros below */
#define SMSC47M1_REG_ALARM 0x04
#define SMSC47M1_REG_TPIN(nr) (0x34 - (nr))
#define SMSC47M1_REG_PPIN(nr) (0x36 - (nr))
#define SMSC47M1_REG_PWM(nr) (0x56 + (nr))
#define SMSC47M1_REG_FANDIV 0x58
#define SMSC47M1_REG_FAN(nr) (0x59 + (nr))
#define SMSC47M1_REG_FAN_PRELOAD(nr) (0x5B + (nr))
#define MIN_FROM_REG(reg,div) ((reg)>=192 ? 0 : \
983040/((192-(reg))*(div)))
#define FAN_FROM_REG(reg,div,preload) ((reg)<=(preload) || (reg)==255 ? 0 : \
983040/(((reg)-(preload))*(div)))
#define DIV_FROM_REG(reg) (1 << (reg))
#define PWM_FROM_REG(reg) (((reg) & 0x7E) << 1)
#define PWM_EN_FROM_REG(reg) ((~(reg)) & 0x01)
#define PWM_TO_REG(reg) (((reg) >> 1) & 0x7E)
struct smsc47m1_data {
struct i2c_client client;
struct semaphore lock;
struct semaphore update_lock;
unsigned long last_updated; /* In jiffies */
u8 fan[2]; /* Register value */
u8 fan_preload[2]; /* Register value */
u8 fan_div[2]; /* Register encoding, shifted right */
u8 alarms; /* Register encoding */
u8 pwm[2]; /* Register value (bit 7 is enable) */
};
static int smsc47m1_attach_adapter(struct i2c_adapter *adapter);
static int smsc47m1_find(int *address);
static int smsc47m1_detect(struct i2c_adapter *adapter, int address, int kind);
static int smsc47m1_detach_client(struct i2c_client *client);
static int smsc47m1_read_value(struct i2c_client *client, u8 reg);
static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value);
static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
int init);
static struct i2c_driver smsc47m1_driver = {
.owner = THIS_MODULE,
.name = "smsc47m1",
.id = I2C_DRIVERID_SMSC47M1,
.flags = I2C_DF_NOTIFY,
.attach_adapter = smsc47m1_attach_adapter,
.detach_client = smsc47m1_detach_client,
};
/* nr is 0 or 1 in the callback functions below */
static ssize_t get_fan(struct device *dev, char *buf, int nr)
{
struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
/* This chip (stupidly) stops monitoring fan speed if PWM is
enabled and duty cycle is 0%. This is fine if the monitoring
and control concern the same fan, but troublesome if they are
not (which could as well happen). */
int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 :
FAN_FROM_REG(data->fan[nr],
DIV_FROM_REG(data->fan_div[nr]),
data->fan_preload[nr]);
return sprintf(buf, "%d\n", rpm);
}
static ssize_t get_fan_min(struct device *dev, char *buf, int nr)
{
struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
int rpm = MIN_FROM_REG(data->fan_preload[nr],
DIV_FROM_REG(data->fan_div[nr]));
return sprintf(buf, "%d\n", rpm);
}
static ssize_t get_fan_div(struct device *dev, char *buf, int nr)
{
struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]));
}
static ssize_t get_pwm(struct device *dev, char *buf, int nr)
{
struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr]));
}
static ssize_t get_pwm_en(struct device *dev, char *buf, int nr)
{
struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[nr]));
}
static ssize_t get_alarms(struct device *dev, struct device_attribute *attr, char *buf)
{
struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
return sprintf(buf, "%d\n", data->alarms);
}
static ssize_t set_fan_min(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct smsc47m1_data *data = i2c_get_clientdata(client);
long rpmdiv, val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]);
if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) {
up(&data->update_lock);
return -EINVAL;
}
data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);
smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr),
data->fan_preload[nr]);
up(&data->update_lock);
return count;
}
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan clock divider. This follows the principle
of least suprise; the user doesn't expect the fan minimum to change just
because the divider changed. */
static ssize_t set_fan_div(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct smsc47m1_data *data = i2c_get_clientdata(client);
long new_div = simple_strtol(buf, NULL, 10), tmp;
u8 old_div = DIV_FROM_REG(data->fan_div[nr]);
if (new_div == old_div) /* No change */
return count;
down(&data->update_lock);
switch (new_div) {
case 1: data->fan_div[nr] = 0; break;
case 2: data->fan_div[nr] = 1; break;
case 4: data->fan_div[nr] = 2; break;
case 8: data->fan_div[nr] = 3; break;
default:
up(&data->update_lock);
return -EINVAL;
}
tmp = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV) & 0x0F;
tmp |= (data->fan_div[0] << 4) | (data->fan_div[1] << 6);
smsc47m1_write_value(client, SMSC47M1_REG_FANDIV, tmp);
/* Preserve fan min */
tmp = 192 - (old_div * (192 - data->fan_preload[nr])
+ new_div / 2) / new_div;
data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191);
smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr),
data->fan_preload[nr]);
up(&data->update_lock);
return count;
}
static ssize_t set_pwm(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct smsc47m1_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
if (val < 0 || val > 255)
return -EINVAL;
down(&data->update_lock);
data->pwm[nr] &= 0x81; /* Preserve additional bits */
data->pwm[nr] |= PWM_TO_REG(val);
smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr),
data->pwm[nr]);
up(&data->update_lock);
return count;
}
static ssize_t set_pwm_en(struct device *dev, const char *buf,
size_t count, int nr)
{
struct i2c_client *client = to_i2c_client(dev);
struct smsc47m1_data *data = i2c_get_clientdata(client);
long val = simple_strtol(buf, NULL, 10);
if (val != 0 && val != 1)
return -EINVAL;
down(&data->update_lock);
data->pwm[nr] &= 0xFE; /* preserve the other bits */
data->pwm[nr] |= !val;
smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr),
data->pwm[nr]);
up(&data->update_lock);
return count;
}
#define fan_present(offset) \
static ssize_t get_fan##offset (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return get_fan(dev, buf, offset - 1); \
} \
static ssize_t get_fan##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return get_fan_min(dev, buf, offset - 1); \
} \
static ssize_t set_fan##offset##_min (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_fan_min(dev, buf, count, offset - 1); \
} \
static ssize_t get_fan##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return get_fan_div(dev, buf, offset - 1); \
} \
static ssize_t set_fan##offset##_div (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_fan_div(dev, buf, count, offset - 1); \
} \
static ssize_t get_pwm##offset (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return get_pwm(dev, buf, offset - 1); \
} \
static ssize_t set_pwm##offset (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_pwm(dev, buf, count, offset - 1); \
} \
static ssize_t get_pwm##offset##_en (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return get_pwm_en(dev, buf, offset - 1); \
} \
static ssize_t set_pwm##offset##_en (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_pwm_en(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan##offset, \
NULL); \
static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
get_fan##offset##_min, set_fan##offset##_min); \
static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
get_fan##offset##_div, set_fan##offset##_div); \
static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
get_pwm##offset, set_pwm##offset); \
static DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
get_pwm##offset##_en, set_pwm##offset##_en);
fan_present(1);
fan_present(2);
static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
static int smsc47m1_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, smsc47m1_detect);
}
static int smsc47m1_find(int *address)
{
u8 val;
superio_enter();
val = superio_inb(SUPERIO_REG_DEVID);
/*
* SMSC LPC47M10x/LPC47M13x (device id 0x59), LPC47M14x (device id
* 0x5F) and LPC47B27x (device id 0x51) have fan control.
* The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
* can do much more besides (device id 0x60).
*/
if (val == 0x51)
printk(KERN_INFO "smsc47m1: Found SMSC LPC47B27x\n");
else if (val == 0x59)
printk(KERN_INFO "smsc47m1: Found SMSC LPC47M10x/LPC47M13x\n");
else if (val == 0x5F)
printk(KERN_INFO "smsc47m1: Found SMSC LPC47M14x\n");
else if (val == 0x60)
printk(KERN_INFO "smsc47m1: Found SMSC LPC47M15x/LPC47M192\n");
else {
superio_exit();
return -ENODEV;
}
superio_select();
*address = (superio_inb(SUPERIO_REG_BASE) << 8)
| superio_inb(SUPERIO_REG_BASE + 1);
val = superio_inb(SUPERIO_REG_ACT);
if (*address == 0 || (val & 0x01) == 0) {
printk(KERN_INFO "smsc47m1: Device is disabled, will not use\n");
superio_exit();
return -ENODEV;
}
superio_exit();
return 0;
}
static int smsc47m1_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct smsc47m1_data *data;
int err = 0;
int fan1, fan2, pwm1, pwm2;
if (!i2c_is_isa_adapter(adapter)) {
return 0;
}
if (!request_region(address, SMSC_EXTENT, smsc47m1_driver.name)) {
dev_err(&adapter->dev, "Region 0x%x already in use!\n", address);
return -EBUSY;
}
if (!(data = kmalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
err = -ENOMEM;
goto error_release;
}
memset(data, 0x00, sizeof(struct smsc47m1_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
init_MUTEX(&data->lock);
new_client->adapter = adapter;
new_client->driver = &smsc47m1_driver;
new_client->flags = 0;
strlcpy(new_client->name, "smsc47m1", I2C_NAME_SIZE);
init_MUTEX(&data->update_lock);
/* If no function is properly configured, there's no point in
actually registering the chip. */
fan1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(0)) & 0x05)
== 0x05;
fan2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(1)) & 0x05)
== 0x05;
pwm1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(0)) & 0x05)
== 0x04;
pwm2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(1)) & 0x05)
== 0x04;
if (!(fan1 || fan2 || pwm1 || pwm2)) {
dev_warn(&new_client->dev, "Device is not configured, will not use\n");
err = -ENODEV;
goto error_free;
}
if ((err = i2c_attach_client(new_client)))
goto error_free;
/* Some values (fan min, clock dividers, pwm registers) may be
needed before any update is triggered, so we better read them
at least once here. We don't usually do it that way, but in
this particular case, manually reading 5 registers out of 8
doesn't make much sense and we're better using the existing
function. */
smsc47m1_update_device(&new_client->dev, 1);
if (fan1) {
device_create_file(&new_client->dev, &dev_attr_fan1_input);
device_create_file(&new_client->dev, &dev_attr_fan1_min);
device_create_file(&new_client->dev, &dev_attr_fan1_div);
} else
dev_dbg(&new_client->dev, "Fan 1 not enabled by hardware, "
"skipping\n");
if (fan2) {
device_create_file(&new_client->dev, &dev_attr_fan2_input);
device_create_file(&new_client->dev, &dev_attr_fan2_min);
device_create_file(&new_client->dev, &dev_attr_fan2_div);
} else
dev_dbg(&new_client->dev, "Fan 2 not enabled by hardware, "
"skipping\n");
if (pwm1) {
device_create_file(&new_client->dev, &dev_attr_pwm1);
device_create_file(&new_client->dev, &dev_attr_pwm1_enable);
} else
dev_dbg(&new_client->dev, "PWM 1 not enabled by hardware, "
"skipping\n");
if (pwm2) {
device_create_file(&new_client->dev, &dev_attr_pwm2);
device_create_file(&new_client->dev, &dev_attr_pwm2_enable);
} else
dev_dbg(&new_client->dev, "PWM 2 not enabled by hardware, "
"skipping\n");
device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
error_free:
kfree(new_client);
error_release:
release_region(address, SMSC_EXTENT);
return err;
}
static int smsc47m1_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, SMSC_EXTENT);
kfree(i2c_get_clientdata(client));
return 0;
}
static int smsc47m1_read_value(struct i2c_client *client, u8 reg)
{
int res;
down(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
res = inb_p(client->addr + reg);
up(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
return res;
}
static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value)
{
down(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
outb_p(value, client->addr + reg);
up(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock);
}
static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
int init)
{
struct i2c_client *client = to_i2c_client(dev);
struct smsc47m1_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
int i;
for (i = 0; i < 2; i++) {
data->fan[i] = smsc47m1_read_value(client,
SMSC47M1_REG_FAN(i));
data->fan_preload[i] = smsc47m1_read_value(client,
SMSC47M1_REG_FAN_PRELOAD(i));
data->pwm[i] = smsc47m1_read_value(client,
SMSC47M1_REG_PWM(i));
}
i = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV);
data->fan_div[0] = (i >> 4) & 0x03;
data->fan_div[1] = i >> 6;
data->alarms = smsc47m1_read_value(client,
SMSC47M1_REG_ALARM) >> 6;
/* Clear alarms if needed */
if (data->alarms)
smsc47m1_write_value(client, SMSC47M1_REG_ALARM, 0xC0);
data->last_updated = jiffies;
}
up(&data->update_lock);
return data;
}
static int __init sm_smsc47m1_init(void)
{
if (smsc47m1_find(normal_isa)) {
return -ENODEV;
}
return i2c_add_driver(&smsc47m1_driver);
}
static void __exit sm_smsc47m1_exit(void)
{
i2c_del_driver(&smsc47m1_driver);
}
MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");
MODULE_LICENSE("GPL");
module_init(sm_smsc47m1_init);
module_exit(sm_smsc47m1_exit);

875
drivers/hwmon/via686a.c Normal file
Näytä tiedosto

@@ -0,0 +1,875 @@
/*
via686a.c - Part of lm_sensors, Linux kernel modules
for hardware monitoring
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
Ky<4B>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.
Warning - only supports a single device.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/jiffies.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 unsigned short force_addr = 0;
module_param(force_addr, ushort, 0);
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[] = { I2C_CLIENT_END };
static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_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))
/* temps numbered 1-3 */
static const u8 VIA686A_REG_TEMP[] = { 0x20, 0x21, 0x1f };
static const u8 VIA686A_REG_TEMP_OVER[] = { 0x39, 0x3d, 0x1d };
static const u8 VIA686A_REG_TEMP_HYST[] = { 0x3a, 0x3e, 0x1e };
/* bits 7-6 */
#define VIA686A_REG_TEMP_LOW1 0x4b
/* 2 = bits 5-4, 3 = bits 7-6 */
#define VIA686A_REG_TEMP_LOW23 0x49
#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. 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>) */
static inline u8 IN_TO_REG(long val, int inNum)
{
/* To avoid floating point, we multiply constants by 10 (100 for +12V).
Rounding is done (120500 is actually 133000 - 12500).
Remember that val is expressed in 0.001V/bit, which is why we divide
by an additional 10000 (100000 for +12V): 1000 for val and 10 (100)
for the constants. */
if (inNum <= 1)
return (u8)
SENSORS_LIMIT((val * 21024 - 1205000) / 250000, 0, 255);
else if (inNum == 2)
return (u8)
SENSORS_LIMIT((val * 15737 - 1205000) / 250000, 0, 255);
else if (inNum == 3)
return (u8)
SENSORS_LIMIT((val * 10108 - 1205000) / 250000, 0, 255);
else
return (u8)
SENSORS_LIMIT((val * 41714 - 12050000) / 2500000, 0, 255);
}
static inline long IN_FROM_REG(u8 val, int inNum)
{
/* To avoid floating point, we multiply constants by 10 (100 for +12V).
We also multiply them by 1000 because we want 0.001V/bit for the
output value. Rounding is done. */
if (inNum <= 1)
return (long) ((250000 * val + 1330000 + 21024 / 2) / 21024);
else if (inNum == 2)
return (long) ((250000 * val + 1330000 + 15737 / 2) / 15737);
else if (inNum == 3)
return (long) ((250000 * val + 1330000 + 10108 / 2) / 10108);
else
return (long) ((2500000 * val + 13300000 + 41714 / 2) / 41714);
}
/********* 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 provide 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.
The +50 is because the temps start at -50 */
static inline u8 TEMP_TO_REG(long val)
{
return viaLUT[val <= -50000 ? 0 : val >= 110000 ? 160 :
(val < 0 ? val - 500 : val + 500) / 1000 + 50];
}
/* for 8-bit temperature hyst and over registers */
#define TEMP_FROM_REG(val) (tempLUT[(val)] * 100)
/* for 10-bit temperature readings */
static inline long TEMP_FROM_REG10(u16 val)
{
u16 eightBits = val >> 2;
u16 twoBits = val & 3;
/* no interpolation for these */
if (twoBits == 0 || eightBits == 255)
return TEMP_FROM_REG(eightBits);
/* do some linear interpolation */
return (tempLUT[eightBits] * (4 - twoBits) +
tempLUT[eightBits + 1] * twoBits) * 25;
}
#define DIV_FROM_REG(val) (1 << (val))
#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
/* For the VIA686A, we need to keep some data in memory.
The structure is dynamically allocated, at the same time when a new
via686a client is allocated. */
struct via686a_data {
struct i2c_client client;
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 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);
}
static struct via686a_data *via686a_update_device(struct device *dev);
static void via686a_init_client(struct i2c_client *client);
/* following are the sysfs callback functions */
/* 7 voltage sensors */
static ssize_t show_in(struct device *dev, char *buf, int nr) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf, "%ld\n", IN_FROM_REG(data->in[nr], nr));
}
static ssize_t show_in_min(struct device *dev, char *buf, int nr) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_min[nr], nr));
}
static ssize_t show_in_max(struct device *dev, char *buf, int nr) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_max[nr], nr));
}
static ssize_t set_in_min(struct device *dev, const char *buf,
size_t count, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->in_min[nr] = IN_TO_REG(val, nr);
via686a_write_value(client, VIA686A_REG_IN_MIN(nr),
data->in_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t set_in_max(struct device *dev, const char *buf,
size_t count, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
unsigned long val = simple_strtoul(buf, NULL, 10);
down(&data->update_lock);
data->in_max[nr] = IN_TO_REG(val, nr);
via686a_write_value(client, VIA686A_REG_IN_MAX(nr),
data->in_max[nr]);
up(&data->update_lock);
return count;
}
#define show_in_offset(offset) \
static ssize_t \
show_in##offset (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_in(dev, buf, offset); \
} \
static ssize_t \
show_in##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_in_min(dev, buf, offset); \
} \
static ssize_t \
show_in##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_in_max(dev, buf, offset); \
} \
static ssize_t set_in##offset##_min (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_in_min(dev, buf, count, offset); \
} \
static ssize_t set_in##offset##_max (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_in_max(dev, buf, count, offset); \
} \
static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in##offset, NULL);\
static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \
show_in##offset##_min, set_in##offset##_min); \
static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \
show_in##offset##_max, set_in##offset##_max);
show_in_offset(0);
show_in_offset(1);
show_in_offset(2);
show_in_offset(3);
show_in_offset(4);
/* 3 temperatures */
static ssize_t show_temp(struct device *dev, char *buf, int nr) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf, "%ld\n", TEMP_FROM_REG10(data->temp[nr]));
}
static ssize_t show_temp_over(struct device *dev, char *buf, int nr) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_over[nr]));
}
static ssize_t show_temp_hyst(struct device *dev, char *buf, int nr) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_hyst[nr]));
}
static ssize_t set_temp_over(struct device *dev, const char *buf,
size_t count, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_over[nr] = TEMP_TO_REG(val);
via686a_write_value(client, VIA686A_REG_TEMP_OVER[nr],
data->temp_over[nr]);
up(&data->update_lock);
return count;
}
static ssize_t set_temp_hyst(struct device *dev, const char *buf,
size_t count, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->temp_hyst[nr] = TEMP_TO_REG(val);
via686a_write_value(client, VIA686A_REG_TEMP_HYST[nr],
data->temp_hyst[nr]);
up(&data->update_lock);
return count;
}
#define show_temp_offset(offset) \
static ssize_t show_temp_##offset (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_temp(dev, buf, offset - 1); \
} \
static ssize_t \
show_temp_##offset##_over (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_temp_over(dev, buf, offset - 1); \
} \
static ssize_t \
show_temp_##offset##_hyst (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_temp_hyst(dev, buf, offset - 1); \
} \
static ssize_t set_temp_##offset##_over (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_temp_over(dev, buf, count, offset - 1); \
} \
static ssize_t set_temp_##offset##_hyst (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_temp_hyst(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, NULL);\
static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \
show_temp_##offset##_over, set_temp_##offset##_over); \
static DEVICE_ATTR(temp##offset##_max_hyst, S_IRUGO | S_IWUSR, \
show_temp_##offset##_hyst, set_temp_##offset##_hyst);
show_temp_offset(1);
show_temp_offset(2);
show_temp_offset(3);
/* 2 Fans */
static ssize_t show_fan(struct device *dev, char *buf, int nr) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
DIV_FROM_REG(data->fan_div[nr])) );
}
static ssize_t show_fan_min(struct device *dev, char *buf, int nr) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf, "%d\n",
FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])) );
}
static ssize_t show_fan_div(struct device *dev, char *buf, int nr) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) );
}
static ssize_t set_fan_min(struct device *dev, const char *buf,
size_t count, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
down(&data->update_lock);
data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
via686a_write_value(client, VIA686A_REG_FAN_MIN(nr+1), data->fan_min[nr]);
up(&data->update_lock);
return count;
}
static ssize_t set_fan_div(struct device *dev, const char *buf,
size_t count, int nr) {
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
int val = simple_strtol(buf, NULL, 10);
int old;
down(&data->update_lock);
old = via686a_read_value(client, VIA686A_REG_FANDIV);
data->fan_div[nr] = DIV_TO_REG(val);
old = (old & 0x0f) | (data->fan_div[1] << 6) | (data->fan_div[0] << 4);
via686a_write_value(client, VIA686A_REG_FANDIV, old);
up(&data->update_lock);
return count;
}
#define show_fan_offset(offset) \
static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_fan(dev, buf, offset - 1); \
} \
static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_fan_min(dev, buf, offset - 1); \
} \
static ssize_t show_fan_##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return show_fan_div(dev, buf, offset - 1); \
} \
static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_fan_min(dev, buf, count, offset - 1); \
} \
static ssize_t set_fan_##offset##_div (struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return set_fan_div(dev, buf, count, offset - 1); \
} \
static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\
static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
show_fan_##offset##_min, set_fan_##offset##_min); \
static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
show_fan_##offset##_div, set_fan_##offset##_div);
show_fan_offset(1);
show_fan_offset(2);
/* Alarms */
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) {
struct via686a_data *data = via686a_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
/* 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,
};
/* This is called when the module is loaded */
static int via686a_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
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 client_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_driver.name)) {
dev_err(&adapter->dev, "region 0x%x already in use!\n",
address);
return -ENODEV;
}
if (!(data = kmalloc(sizeof(struct via686a_data), GFP_KERNEL))) {
err = -ENOMEM;
goto ERROR0;
}
memset(data, 0, sizeof(struct via686a_data));
new_client = &data->client;
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 */
strlcpy(new_client->name, client_name, I2C_NAME_SIZE);
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);
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_in0_input);
device_create_file(&new_client->dev, &dev_attr_in1_input);
device_create_file(&new_client->dev, &dev_attr_in2_input);
device_create_file(&new_client->dev, &dev_attr_in3_input);
device_create_file(&new_client->dev, &dev_attr_in4_input);
device_create_file(&new_client->dev, &dev_attr_in0_min);
device_create_file(&new_client->dev, &dev_attr_in1_min);
device_create_file(&new_client->dev, &dev_attr_in2_min);
device_create_file(&new_client->dev, &dev_attr_in3_min);
device_create_file(&new_client->dev, &dev_attr_in4_min);
device_create_file(&new_client->dev, &dev_attr_in0_max);
device_create_file(&new_client->dev, &dev_attr_in1_max);
device_create_file(&new_client->dev, &dev_attr_in2_max);
device_create_file(&new_client->dev, &dev_attr_in3_max);
device_create_file(&new_client->dev, &dev_attr_in4_max);
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp2_input);
device_create_file(&new_client->dev, &dev_attr_temp3_input);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
device_create_file(&new_client->dev, &dev_attr_temp2_max);
device_create_file(&new_client->dev, &dev_attr_temp3_max);
device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
device_create_file(&new_client->dev, &dev_attr_temp2_max_hyst);
device_create_file(&new_client->dev, &dev_attr_temp3_max_hyst);
device_create_file(&new_client->dev, &dev_attr_fan1_input);
device_create_file(&new_client->dev, &dev_attr_fan2_input);
device_create_file(&new_client->dev, &dev_attr_fan1_min);
device_create_file(&new_client->dev, &dev_attr_fan2_min);
device_create_file(&new_client->dev, &dev_attr_fan1_div);
device_create_file(&new_client->dev, &dev_attr_fan2_div);
device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
ERROR3:
kfree(data);
ERROR0:
release_region(address, VIA686A_EXTENT);
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(i2c_get_clientdata(client));
return 0;
}
/* Called when we have found a new VIA686A. Set limits, etc. */
static void via686a_init_client(struct i2c_client *client)
{
u8 reg;
/* Start monitoring */
reg = via686a_read_value(client, VIA686A_REG_CONFIG);
via686a_write_value(client, VIA686A_REG_CONFIG, (reg|0x01)&0x7F);
/* Configure 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 struct via686a_data *via686a_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct via686a_data *data = i2c_get_clientdata(client);
int i;
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !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 = 0; i <= 2; i++) {
data->temp[i] = via686a_read_value(client,
VIA686A_REG_TEMP[i]) << 2;
data->temp_over[i] =
via686a_read_value(client,
VIA686A_REG_TEMP_OVER[i]);
data->temp_hyst[i] =
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);
return data;
}
static struct pci_device_id via686a_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, via686a_pci_ids);
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 = pci_dev_get(dev);
if (i2c_add_driver(&via686a_driver)) {
pci_dev_put(s_bridge);
s_bridge = NULL;
}
/* Always return failure here. This is to allow other drivers to bind
* to this pci device. We don't really want to have control over the
* pci device, we only wanted to read as few register values from it.
*/
return -ENODEV;
}
static struct pci_driver via686a_pci_driver = {
.name = "via686a",
.id_table = via686a_pci_ids,
.probe = via686a_pci_probe,
};
static int __init sm_via686a_init(void)
{
return pci_register_driver(&via686a_pci_driver);
}
static void __exit sm_via686a_exit(void)
{
pci_unregister_driver(&via686a_pci_driver);
if (s_bridge != NULL) {
i2c_del_driver(&via686a_driver);
pci_dev_put(s_bridge);
s_bridge = NULL;
}
}
MODULE_AUTHOR("Ky<EFBFBD>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);

846
drivers/hwmon/w83627ehf.c Normal file
Näytä tiedosto

@@ -0,0 +1,846 @@
/*
w83627ehf - Driver for the hardware monitoring functionality of
the Winbond W83627EHF Super-I/O chip
Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
Shamelessly ripped from the w83627hf driver
Copyright (C) 2003 Mark Studebaker
Thanks to Leon Moonen, Steve Cliffe and Grant Coady for their help
in testing and debugging this driver.
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 following chips:
Chip #vin #fan #pwm #temp chip_id man_id
w83627ehf - 5 - 3 0x88 0x5ca3
This is a preliminary version of the driver, only supporting the
fan and temperature inputs. The chip does much more than that.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
#include <asm/io.h>
#include "lm75.h"
/* Addresses to scan
The actual ISA address is read from Super-I/O configuration space */
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
static unsigned int normal_isa[] = { 0, I2C_CLIENT_ISA_END };
/* Insmod parameters */
SENSORS_INSMOD_1(w83627ehf);
/*
* Super-I/O constants and functions
*/
static int REG; /* The register to read/write */
static int VAL; /* The value to read/write */
#define W83627EHF_LD_HWM 0x0b
#define SIO_REG_LDSEL 0x07 /* Logical device select */
#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
#define SIO_REG_ENABLE 0x30 /* Logical device enable */
#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
#define SIO_W83627EHF_ID 0x8840
#define SIO_ID_MASK 0xFFC0
static inline void
superio_outb(int reg, int val)
{
outb(reg, REG);
outb(val, VAL);
}
static inline int
superio_inb(int reg)
{
outb(reg, REG);
return inb(VAL);
}
static inline void
superio_select(int ld)
{
outb(SIO_REG_LDSEL, REG);
outb(ld, VAL);
}
static inline void
superio_enter(void)
{
outb(0x87, REG);
outb(0x87, REG);
}
static inline void
superio_exit(void)
{
outb(0x02, REG);
outb(0x02, VAL);
}
/*
* ISA constants
*/
#define REGION_LENGTH 8
#define ADDR_REG_OFFSET 5
#define DATA_REG_OFFSET 6
#define W83627EHF_REG_BANK 0x4E
#define W83627EHF_REG_CONFIG 0x40
#define W83627EHF_REG_CHIP_ID 0x49
#define W83627EHF_REG_MAN_ID 0x4F
static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 };
static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c };
#define W83627EHF_REG_TEMP1 0x27
#define W83627EHF_REG_TEMP1_HYST 0x3a
#define W83627EHF_REG_TEMP1_OVER 0x39
static const u16 W83627EHF_REG_TEMP[] = { 0x150, 0x250 };
static const u16 W83627EHF_REG_TEMP_HYST[] = { 0x153, 0x253 };
static const u16 W83627EHF_REG_TEMP_OVER[] = { 0x155, 0x255 };
static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 };
/* Fan clock dividers are spread over the following five registers */
#define W83627EHF_REG_FANDIV1 0x47
#define W83627EHF_REG_FANDIV2 0x4B
#define W83627EHF_REG_VBAT 0x5D
#define W83627EHF_REG_DIODE 0x59
#define W83627EHF_REG_SMI_OVT 0x4C
/*
* Conversions
*/
static inline unsigned int
fan_from_reg(u8 reg, unsigned int div)
{
if (reg == 0 || reg == 255)
return 0;
return 1350000U / (reg * div);
}
static inline unsigned int
div_from_reg(u8 reg)
{
return 1 << reg;
}
static inline int
temp1_from_reg(s8 reg)
{
return reg * 1000;
}
static inline s8
temp1_to_reg(int temp)
{
if (temp <= -128000)
return -128;
if (temp >= 127000)
return 127;
if (temp < 0)
return (temp - 500) / 1000;
return (temp + 500) / 1000;
}
/*
* Data structures and manipulation thereof
*/
struct w83627ehf_data {
struct i2c_client client;
struct semaphore lock;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
/* Register values */
u8 fan[5];
u8 fan_min[5];
u8 fan_div[5];
u8 has_fan; /* some fan inputs can be disabled */
s8 temp1;
s8 temp1_max;
s8 temp1_max_hyst;
s16 temp[2];
s16 temp_max[2];
s16 temp_max_hyst[2];
};
static inline int is_word_sized(u16 reg)
{
return (((reg & 0xff00) == 0x100
|| (reg & 0xff00) == 0x200)
&& ((reg & 0x00ff) == 0x50
|| (reg & 0x00ff) == 0x53
|| (reg & 0x00ff) == 0x55));
}
/* We assume that the default bank is 0, thus the following two functions do
nothing for registers which live in bank 0. For others, they respectively
set the bank register to the correct value (before the register is
accessed), and back to 0 (afterwards). */
static inline void w83627ehf_set_bank(struct i2c_client *client, u16 reg)
{
if (reg & 0xff00) {
outb_p(W83627EHF_REG_BANK, client->addr + ADDR_REG_OFFSET);
outb_p(reg >> 8, client->addr + DATA_REG_OFFSET);
}
}
static inline void w83627ehf_reset_bank(struct i2c_client *client, u16 reg)
{
if (reg & 0xff00) {
outb_p(W83627EHF_REG_BANK, client->addr + ADDR_REG_OFFSET);
outb_p(0, client->addr + DATA_REG_OFFSET);
}
}
static u16 w83627ehf_read_value(struct i2c_client *client, u16 reg)
{
struct w83627ehf_data *data = i2c_get_clientdata(client);
int res, word_sized = is_word_sized(reg);
down(&data->lock);
w83627ehf_set_bank(client, reg);
outb_p(reg & 0xff, client->addr + ADDR_REG_OFFSET);
res = inb_p(client->addr + DATA_REG_OFFSET);
if (word_sized) {
outb_p((reg & 0xff) + 1,
client->addr + ADDR_REG_OFFSET);
res = (res << 8) + inb_p(client->addr + DATA_REG_OFFSET);
}
w83627ehf_reset_bank(client, reg);
up(&data->lock);
return res;
}
static int w83627ehf_write_value(struct i2c_client *client, u16 reg, u16 value)
{
struct w83627ehf_data *data = i2c_get_clientdata(client);
int word_sized = is_word_sized(reg);
down(&data->lock);
w83627ehf_set_bank(client, reg);
outb_p(reg & 0xff, client->addr + ADDR_REG_OFFSET);
if (word_sized) {
outb_p(value >> 8, client->addr + DATA_REG_OFFSET);
outb_p((reg & 0xff) + 1,
client->addr + ADDR_REG_OFFSET);
}
outb_p(value & 0xff, client->addr + DATA_REG_OFFSET);
w83627ehf_reset_bank(client, reg);
up(&data->lock);
return 0;
}
/* This function assumes that the caller holds data->update_lock */
static void w83627ehf_write_fan_div(struct i2c_client *client, int nr)
{
struct w83627ehf_data *data = i2c_get_clientdata(client);
u8 reg;
switch (nr) {
case 0:
reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV1) & 0xcf)
| ((data->fan_div[0] & 0x03) << 4);
w83627ehf_write_value(client, W83627EHF_REG_FANDIV1, reg);
reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0xdf)
| ((data->fan_div[0] & 0x04) << 3);
w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg);
break;
case 1:
reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV1) & 0x3f)
| ((data->fan_div[1] & 0x03) << 6);
w83627ehf_write_value(client, W83627EHF_REG_FANDIV1, reg);
reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0xbf)
| ((data->fan_div[1] & 0x04) << 4);
w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg);
break;
case 2:
reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV2) & 0x3f)
| ((data->fan_div[2] & 0x03) << 6);
w83627ehf_write_value(client, W83627EHF_REG_FANDIV2, reg);
reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0x7f)
| ((data->fan_div[2] & 0x04) << 5);
w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg);
break;
case 3:
reg = (w83627ehf_read_value(client, W83627EHF_REG_DIODE) & 0xfc)
| (data->fan_div[3] & 0x03);
w83627ehf_write_value(client, W83627EHF_REG_DIODE, reg);
reg = (w83627ehf_read_value(client, W83627EHF_REG_SMI_OVT) & 0x7f)
| ((data->fan_div[3] & 0x04) << 5);
w83627ehf_write_value(client, W83627EHF_REG_SMI_OVT, reg);
break;
case 4:
reg = (w83627ehf_read_value(client, W83627EHF_REG_DIODE) & 0x73)
| ((data->fan_div[4] & 0x03) << 3)
| ((data->fan_div[4] & 0x04) << 5);
w83627ehf_write_value(client, W83627EHF_REG_DIODE, reg);
break;
}
}
static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83627ehf_data *data = i2c_get_clientdata(client);
int i;
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ)
|| !data->valid) {
/* Fan clock dividers */
i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1);
data->fan_div[0] = (i >> 4) & 0x03;
data->fan_div[1] = (i >> 6) & 0x03;
i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV2);
data->fan_div[2] = (i >> 6) & 0x03;
i = w83627ehf_read_value(client, W83627EHF_REG_VBAT);
data->fan_div[0] |= (i >> 3) & 0x04;
data->fan_div[1] |= (i >> 4) & 0x04;
data->fan_div[2] |= (i >> 5) & 0x04;
if (data->has_fan & ((1 << 3) | (1 << 4))) {
i = w83627ehf_read_value(client, W83627EHF_REG_DIODE);
data->fan_div[3] = i & 0x03;
data->fan_div[4] = ((i >> 2) & 0x03)
| ((i >> 5) & 0x04);
}
if (data->has_fan & (1 << 3)) {
i = w83627ehf_read_value(client, W83627EHF_REG_SMI_OVT);
data->fan_div[3] |= (i >> 5) & 0x04;
}
/* Measured fan speeds and limits */
for (i = 0; i < 5; i++) {
if (!(data->has_fan & (1 << i)))
continue;
data->fan[i] = w83627ehf_read_value(client,
W83627EHF_REG_FAN[i]);
data->fan_min[i] = w83627ehf_read_value(client,
W83627EHF_REG_FAN_MIN[i]);
/* If we failed to measure the fan speed and clock
divider can be increased, let's try that for next
time */
if (data->fan[i] == 0xff
&& data->fan_div[i] < 0x07) {
dev_dbg(&client->dev, "Increasing fan %d "
"clock divider from %u to %u\n",
i, div_from_reg(data->fan_div[i]),
div_from_reg(data->fan_div[i] + 1));
data->fan_div[i]++;
w83627ehf_write_fan_div(client, i);
/* Preserve min limit if possible */
if (data->fan_min[i] >= 2
&& data->fan_min[i] != 255)
w83627ehf_write_value(client,
W83627EHF_REG_FAN_MIN[i],
(data->fan_min[i] /= 2));
}
}
/* Measured temperatures and limits */
data->temp1 = w83627ehf_read_value(client,
W83627EHF_REG_TEMP1);
data->temp1_max = w83627ehf_read_value(client,
W83627EHF_REG_TEMP1_OVER);
data->temp1_max_hyst = w83627ehf_read_value(client,
W83627EHF_REG_TEMP1_HYST);
for (i = 0; i < 2; i++) {
data->temp[i] = w83627ehf_read_value(client,
W83627EHF_REG_TEMP[i]);
data->temp_max[i] = w83627ehf_read_value(client,
W83627EHF_REG_TEMP_OVER[i]);
data->temp_max_hyst[i] = w83627ehf_read_value(client,
W83627EHF_REG_TEMP_HYST[i]);
}
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
/*
* Sysfs callback functions
*/
#define show_fan_reg(reg) \
static ssize_t \
show_##reg(struct device *dev, char *buf, int nr) \
{ \
struct w83627ehf_data *data = w83627ehf_update_device(dev); \
return sprintf(buf, "%d\n", \
fan_from_reg(data->reg[nr], \
div_from_reg(data->fan_div[nr]))); \
}
show_fan_reg(fan);
show_fan_reg(fan_min);
static ssize_t
show_fan_div(struct device *dev, char *buf, int nr)
{
struct w83627ehf_data *data = w83627ehf_update_device(dev);
return sprintf(buf, "%u\n",
div_from_reg(data->fan_div[nr]));
}
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 w83627ehf_data *data = i2c_get_clientdata(client);
unsigned int val = simple_strtoul(buf, NULL, 10);
unsigned int reg;
u8 new_div;
down(&data->update_lock);
if (!val) {
/* No min limit, alarm disabled */
data->fan_min[nr] = 255;
new_div = data->fan_div[nr]; /* No change */
dev_info(dev, "fan%u low limit and alarm disabled\n", nr + 1);
} else if ((reg = 1350000U / val) >= 128 * 255) {
/* Speed below this value cannot possibly be represented,
even with the highest divider (128) */
data->fan_min[nr] = 254;
new_div = 7; /* 128 == (1 << 7) */
dev_warn(dev, "fan%u low limit %u below minimum %u, set to "
"minimum\n", nr + 1, val, fan_from_reg(254, 128));
} else if (!reg) {
/* Speed above this value cannot possibly be represented,
even with the lowest divider (1) */
data->fan_min[nr] = 1;
new_div = 0; /* 1 == (1 << 0) */
dev_warn(dev, "fan%u low limit %u above maximum %u, set to "
"maximum\n", nr + 1, val, fan_from_reg(1, 1));
} else {
/* Automatically pick the best divider, i.e. the one such
that the min limit will correspond to a register value
in the 96..192 range */
new_div = 0;
while (reg > 192 && new_div < 7) {
reg >>= 1;
new_div++;
}
data->fan_min[nr] = reg;
}
/* Write both the fan clock divider (if it changed) and the new
fan min (unconditionally) */
if (new_div != data->fan_div[nr]) {
if (new_div > data->fan_div[nr])
data->fan[nr] >>= (data->fan_div[nr] - new_div);
else
data->fan[nr] <<= (new_div - data->fan_div[nr]);
dev_dbg(dev, "fan%u clock divider changed from %u to %u\n",
nr + 1, div_from_reg(data->fan_div[nr]),
div_from_reg(new_div));
data->fan_div[nr] = new_div;
w83627ehf_write_fan_div(client, nr);
}
w83627ehf_write_value(client, W83627EHF_REG_FAN_MIN[nr],
data->fan_min[nr]);
up(&data->update_lock);
return count;
}
#define sysfs_fan_offset(offset) \
static ssize_t \
show_reg_fan_##offset(struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
return show_fan(dev, buf, offset-1); \
} \
static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \
show_reg_fan_##offset, NULL);
#define sysfs_fan_min_offset(offset) \
static ssize_t \
show_reg_fan##offset##_min(struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
return show_fan_min(dev, buf, offset-1); \
} \
static ssize_t \
store_reg_fan##offset##_min(struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return store_fan_min(dev, buf, count, offset-1); \
} \
static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
show_reg_fan##offset##_min, \
store_reg_fan##offset##_min);
#define sysfs_fan_div_offset(offset) \
static ssize_t \
show_reg_fan##offset##_div(struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
return show_fan_div(dev, buf, offset - 1); \
} \
static DEVICE_ATTR(fan##offset##_div, S_IRUGO, \
show_reg_fan##offset##_div, NULL);
sysfs_fan_offset(1);
sysfs_fan_min_offset(1);
sysfs_fan_div_offset(1);
sysfs_fan_offset(2);
sysfs_fan_min_offset(2);
sysfs_fan_div_offset(2);
sysfs_fan_offset(3);
sysfs_fan_min_offset(3);
sysfs_fan_div_offset(3);
sysfs_fan_offset(4);
sysfs_fan_min_offset(4);
sysfs_fan_div_offset(4);
sysfs_fan_offset(5);
sysfs_fan_min_offset(5);
sysfs_fan_div_offset(5);
#define show_temp1_reg(reg) \
static ssize_t \
show_##reg(struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
struct w83627ehf_data *data = w83627ehf_update_device(dev); \
return sprintf(buf, "%d\n", temp1_from_reg(data->reg)); \
}
show_temp1_reg(temp1);
show_temp1_reg(temp1_max);
show_temp1_reg(temp1_max_hyst);
#define store_temp1_reg(REG, reg) \
static ssize_t \
store_temp1_##reg(struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct w83627ehf_data *data = i2c_get_clientdata(client); \
u32 val = simple_strtoul(buf, NULL, 10); \
\
down(&data->update_lock); \
data->temp1_##reg = temp1_to_reg(val); \
w83627ehf_write_value(client, W83627EHF_REG_TEMP1_##REG, \
data->temp1_##reg); \
up(&data->update_lock); \
return count; \
}
store_temp1_reg(OVER, max);
store_temp1_reg(HYST, max_hyst);
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp1, NULL);
static DEVICE_ATTR(temp1_max, S_IRUGO| S_IWUSR,
show_temp1_max, store_temp1_max);
static DEVICE_ATTR(temp1_max_hyst, S_IRUGO| S_IWUSR,
show_temp1_max_hyst, store_temp1_max_hyst);
#define show_temp_reg(reg) \
static ssize_t \
show_##reg (struct device *dev, char *buf, int nr) \
{ \
struct w83627ehf_data *data = w83627ehf_update_device(dev); \
return sprintf(buf, "%d\n", \
LM75_TEMP_FROM_REG(data->reg[nr])); \
}
show_temp_reg(temp);
show_temp_reg(temp_max);
show_temp_reg(temp_max_hyst);
#define store_temp_reg(REG, reg) \
static ssize_t \
store_##reg (struct device *dev, const char *buf, size_t count, int nr) \
{ \
struct i2c_client *client = to_i2c_client(dev); \
struct w83627ehf_data *data = i2c_get_clientdata(client); \
u32 val = simple_strtoul(buf, NULL, 10); \
\
down(&data->update_lock); \
data->reg[nr] = LM75_TEMP_TO_REG(val); \
w83627ehf_write_value(client, W83627EHF_REG_TEMP_##REG[nr], \
data->reg[nr]); \
up(&data->update_lock); \
return count; \
}
store_temp_reg(OVER, temp_max);
store_temp_reg(HYST, temp_max_hyst);
#define sysfs_temp_offset(offset) \
static ssize_t \
show_reg_temp##offset (struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
return show_temp(dev, buf, offset - 2); \
} \
static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \
show_reg_temp##offset, NULL);
#define sysfs_temp_reg_offset(reg, offset) \
static ssize_t \
show_reg_temp##offset##_##reg(struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
return show_temp_##reg(dev, buf, offset - 2); \
} \
static ssize_t \
store_reg_temp##offset##_##reg(struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
return store_temp_##reg(dev, buf, count, offset - 2); \
} \
static DEVICE_ATTR(temp##offset##_##reg, S_IRUGO| S_IWUSR, \
show_reg_temp##offset##_##reg, \
store_reg_temp##offset##_##reg);
sysfs_temp_offset(2);
sysfs_temp_reg_offset(max, 2);
sysfs_temp_reg_offset(max_hyst, 2);
sysfs_temp_offset(3);
sysfs_temp_reg_offset(max, 3);
sysfs_temp_reg_offset(max_hyst, 3);
/*
* Driver and client management
*/
static struct i2c_driver w83627ehf_driver;
static void w83627ehf_init_client(struct i2c_client *client)
{
int i;
u8 tmp;
/* Start monitoring is needed */
tmp = w83627ehf_read_value(client, W83627EHF_REG_CONFIG);
if (!(tmp & 0x01))
w83627ehf_write_value(client, W83627EHF_REG_CONFIG,
tmp | 0x01);
/* Enable temp2 and temp3 if needed */
for (i = 0; i < 2; i++) {
tmp = w83627ehf_read_value(client,
W83627EHF_REG_TEMP_CONFIG[i]);
if (tmp & 0x01)
w83627ehf_write_value(client,
W83627EHF_REG_TEMP_CONFIG[i],
tmp & 0xfe);
}
}
static int w83627ehf_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *client;
struct w83627ehf_data *data;
int i, err = 0;
if (!i2c_is_isa_adapter(adapter))
return 0;
if (!request_region(address, REGION_LENGTH, w83627ehf_driver.name)) {
err = -EBUSY;
goto exit;
}
if (!(data = kmalloc(sizeof(struct w83627ehf_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit_release;
}
memset(data, 0, sizeof(struct w83627ehf_data));
client = &data->client;
i2c_set_clientdata(client, data);
client->addr = address;
init_MUTEX(&data->lock);
client->adapter = adapter;
client->driver = &w83627ehf_driver;
client->flags = 0;
strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
data->valid = 0;
init_MUTEX(&data->update_lock);
/* Tell the i2c layer a new client has arrived */
if ((err = i2c_attach_client(client)))
goto exit_free;
/* Initialize the chip */
w83627ehf_init_client(client);
/* A few vars need to be filled upon startup */
for (i = 0; i < 5; i++)
data->fan_min[i] = w83627ehf_read_value(client,
W83627EHF_REG_FAN_MIN[i]);
/* It looks like fan4 and fan5 pins can be alternatively used
as fan on/off switches */
data->has_fan = 0x07; /* fan1, fan2 and fan3 */
i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1);
if (i & (1 << 2))
data->has_fan |= (1 << 3);
if (i & (1 << 0))
data->has_fan |= (1 << 4);
/* Register sysfs hooks */
device_create_file(&client->dev, &dev_attr_fan1_input);
device_create_file(&client->dev, &dev_attr_fan1_min);
device_create_file(&client->dev, &dev_attr_fan1_div);
device_create_file(&client->dev, &dev_attr_fan2_input);
device_create_file(&client->dev, &dev_attr_fan2_min);
device_create_file(&client->dev, &dev_attr_fan2_div);
device_create_file(&client->dev, &dev_attr_fan3_input);
device_create_file(&client->dev, &dev_attr_fan3_min);
device_create_file(&client->dev, &dev_attr_fan3_div);
if (data->has_fan & (1 << 3)) {
device_create_file(&client->dev, &dev_attr_fan4_input);
device_create_file(&client->dev, &dev_attr_fan4_min);
device_create_file(&client->dev, &dev_attr_fan4_div);
}
if (data->has_fan & (1 << 4)) {
device_create_file(&client->dev, &dev_attr_fan5_input);
device_create_file(&client->dev, &dev_attr_fan5_min);
device_create_file(&client->dev, &dev_attr_fan5_div);
}
device_create_file(&client->dev, &dev_attr_temp1_input);
device_create_file(&client->dev, &dev_attr_temp1_max);
device_create_file(&client->dev, &dev_attr_temp1_max_hyst);
device_create_file(&client->dev, &dev_attr_temp2_input);
device_create_file(&client->dev, &dev_attr_temp2_max);
device_create_file(&client->dev, &dev_attr_temp2_max_hyst);
device_create_file(&client->dev, &dev_attr_temp3_input);
device_create_file(&client->dev, &dev_attr_temp3_max);
device_create_file(&client->dev, &dev_attr_temp3_max_hyst);
return 0;
exit_free:
kfree(data);
exit_release:
release_region(address, REGION_LENGTH);
exit:
return err;
}
static int w83627ehf_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, w83627ehf_detect);
}
static int w83627ehf_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, REGION_LENGTH);
kfree(i2c_get_clientdata(client));
return 0;
}
static struct i2c_driver w83627ehf_driver = {
.owner = THIS_MODULE,
.name = "w83627ehf",
.flags = I2C_DF_NOTIFY,
.attach_adapter = w83627ehf_attach_adapter,
.detach_client = w83627ehf_detach_client,
};
static int __init w83627ehf_find(int sioaddr, int *address)
{
u16 val;
REG = sioaddr;
VAL = sioaddr + 1;
superio_enter();
val = (superio_inb(SIO_REG_DEVID) << 8)
| superio_inb(SIO_REG_DEVID + 1);
if ((val & SIO_ID_MASK) != SIO_W83627EHF_ID) {
superio_exit();
return -ENODEV;
}
superio_select(W83627EHF_LD_HWM);
val = (superio_inb(SIO_REG_ADDR) << 8)
| superio_inb(SIO_REG_ADDR + 1);
*address = val & ~(REGION_LENGTH - 1);
if (*address == 0) {
superio_exit();
return -ENODEV;
}
/* Activate logical device if needed */
val = superio_inb(SIO_REG_ENABLE);
if (!(val & 0x01))
superio_outb(SIO_REG_ENABLE, val | 0x01);
superio_exit();
return 0;
}
static int __init sensors_w83627ehf_init(void)
{
if (w83627ehf_find(0x2e, &normal_isa[0])
&& w83627ehf_find(0x4e, &normal_isa[0]))
return -ENODEV;
return i2c_add_driver(&w83627ehf_driver);
}
static void __exit sensors_w83627ehf_exit(void)
{
i2c_del_driver(&w83627ehf_driver);
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("W83627EHF driver");
MODULE_LICENSE("GPL");
module_init(sensors_w83627ehf_init);
module_exit(sensors_w83627ehf_exit);

1511
drivers/hwmon/w83627hf.c Normal file

File diff suppressed because it is too large Load Diff

1632
drivers/hwmon/w83781d.c Normal file

File diff suppressed because it is too large Load Diff

328
drivers/hwmon/w83l785ts.c Normal file
Näytä tiedosto

@@ -0,0 +1,328 @@
/*
* w83l785ts.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
* Copyright (C) 2003-2004 Jean Delvare <khali@linux-fr.org>
*
* Inspired from the lm83 driver. The W83L785TS-S is a sensor chip made
* by Winbond. It reports a single external temperature with a 1 deg
* resolution and a 3 deg accuracy. Datasheet can be obtained from
* Winbond's website at:
* http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83L785TS-S.pdf
*
* Ported to Linux 2.6 by Wolfgang Ziegler <nuppla@gmx.at> and Jean Delvare
* <khali@linux-fr.org>.
*
* Thanks to James Bolt <james@evilpenguin.com> for benchmarking the read
* error handling mechanism.
*
* 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.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-sensor.h>
/* How many retries on register read error */
#define MAX_RETRIES 5
/*
* Address to scan
* Address is fully defined internally and cannot be changed.
*/
static unsigned short normal_i2c[] = { 0x2e, I2C_CLIENT_END };
static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
/*
* Insmod parameters
*/
SENSORS_INSMOD_1(w83l785ts);
/*
* The W83L785TS-S registers
* Manufacturer ID is 0x5CA3 for Winbond.
*/
#define W83L785TS_REG_MAN_ID1 0x4D
#define W83L785TS_REG_MAN_ID2 0x4C
#define W83L785TS_REG_CHIP_ID 0x4E
#define W83L785TS_REG_CONFIG 0x40
#define W83L785TS_REG_TYPE 0x52
#define W83L785TS_REG_TEMP 0x27
#define W83L785TS_REG_TEMP_OVER 0x53 /* not sure about this one */
/*
* Conversions
* The W83L785TS-S uses signed 8-bit values.
*/
#define TEMP_FROM_REG(val) ((val & 0x80 ? val-0x100 : val) * 1000)
/*
* Functions declaration
*/
static int w83l785ts_attach_adapter(struct i2c_adapter *adapter);
static int w83l785ts_detect(struct i2c_adapter *adapter, int address,
int kind);
static int w83l785ts_detach_client(struct i2c_client *client);
static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval);
static struct w83l785ts_data *w83l785ts_update_device(struct device *dev);
/*
* Driver data (common to all clients)
*/
static struct i2c_driver w83l785ts_driver = {
.owner = THIS_MODULE,
.name = "w83l785ts",
.id = I2C_DRIVERID_W83L785TS,
.flags = I2C_DF_NOTIFY,
.attach_adapter = w83l785ts_attach_adapter,
.detach_client = w83l785ts_detach_client,
};
/*
* Client data (each client gets its own)
*/
struct w83l785ts_data {
struct i2c_client client;
struct semaphore update_lock;
char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
/* registers values */
u8 temp, temp_over;
};
/*
* Sysfs stuff
*/
static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf)
{
struct w83l785ts_data *data = w83l785ts_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp));
}
static ssize_t show_temp_over(struct device *dev, struct device_attribute *attr, char *buf)
{
struct w83l785ts_data *data = w83l785ts_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over));
}
static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
static DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_over, NULL);
/*
* Real code
*/
static int w83l785ts_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
return 0;
return i2c_detect(adapter, &addr_data, w83l785ts_detect);
}
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
*/
static int w83l785ts_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
struct w83l785ts_data *data;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
goto exit;
if (!(data = kmalloc(sizeof(struct w83l785ts_data), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(data, 0, sizeof(struct w83l785ts_data));
/* The common I2C client data is placed right before the
* W83L785TS-specific data. */
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &w83l785ts_driver;
new_client->flags = 0;
/*
* Now we do the remaining detection. A negative kind means that
* the driver was loaded with no force parameter (default), so we
* must both detect and identify the chip (actually there is only
* one possible kind of chip for now, W83L785TS-S). A zero kind means
* that the driver was loaded with the force parameter, the detection
* step shall be skipped. A positive kind means that the driver
* was loaded with the force parameter and a given kind of chip is
* requested, so both the detection and the identification steps
* are skipped.
*/
if (kind < 0) { /* detection */
if (((w83l785ts_read_value(new_client,
W83L785TS_REG_CONFIG, 0) & 0x80) != 0x00)
|| ((w83l785ts_read_value(new_client,
W83L785TS_REG_TYPE, 0) & 0xFC) != 0x00)) {
dev_dbg(&adapter->dev,
"W83L785TS-S detection failed at 0x%02x.\n",
address);
goto exit_free;
}
}
if (kind <= 0) { /* identification */
u16 man_id;
u8 chip_id;
man_id = (w83l785ts_read_value(new_client,
W83L785TS_REG_MAN_ID1, 0) << 8) +
w83l785ts_read_value(new_client,
W83L785TS_REG_MAN_ID2, 0);
chip_id = w83l785ts_read_value(new_client,
W83L785TS_REG_CHIP_ID, 0);
if (man_id == 0x5CA3) { /* Winbond */
if (chip_id == 0x70) { /* W83L785TS-S */
kind = w83l785ts;
}
}
if (kind <= 0) { /* identification failed */
dev_info(&adapter->dev,
"Unsupported chip (man_id=0x%04X, "
"chip_id=0x%02X).\n", man_id, chip_id);
goto exit_free;
}
}
/* We can fill in the remaining client fields. */
strlcpy(new_client->name, "w83l785ts", I2C_NAME_SIZE);
data->valid = 0;
init_MUTEX(&data->update_lock);
/* Default values in case the first read fails (unlikely). */
data->temp_over = data->temp = 0;
/* Tell the I2C layer a new client has arrived. */
if ((err = i2c_attach_client(new_client)))
goto exit_free;
/*
* Initialize the W83L785TS chip
* Nothing yet, assume it is already started.
*/
/* Register sysfs hooks */
device_create_file(&new_client->dev, &dev_attr_temp1_input);
device_create_file(&new_client->dev, &dev_attr_temp1_max);
return 0;
exit_free:
kfree(data);
exit:
return err;
}
static int w83l785ts_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;
}
kfree(i2c_get_clientdata(client));
return 0;
}
static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval)
{
int value, i;
/* Frequent read errors have been reported on Asus boards, so we
* retry on read errors. If it still fails (unlikely), return the
* default value requested by the caller. */
for (i = 1; i <= MAX_RETRIES; i++) {
value = i2c_smbus_read_byte_data(client, reg);
if (value >= 0) {
dev_dbg(&client->dev, "Read 0x%02x from register "
"0x%02x.\n", value, reg);
return value;
}
dev_dbg(&client->dev, "Read failed, will retry in %d.\n", i);
msleep(i);
}
dev_err(&client->dev, "Couldn't read value from register 0x%02x. "
"Please report.\n", reg);
return defval;
}
static struct w83l785ts_data *w83l785ts_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83l785ts_data *data = i2c_get_clientdata(client);
down(&data->update_lock);
if (!data->valid || time_after(jiffies, data->last_updated + HZ * 2)) {
dev_dbg(&client->dev, "Updating w83l785ts data.\n");
data->temp = w83l785ts_read_value(client,
W83L785TS_REG_TEMP, data->temp);
data->temp_over = w83l785ts_read_value(client,
W83L785TS_REG_TEMP_OVER, data->temp_over);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
return data;
}
static int __init sensors_w83l785ts_init(void)
{
return i2c_add_driver(&w83l785ts_driver);
}
static void __exit sensors_w83l785ts_exit(void)
{
i2c_del_driver(&w83l785ts_driver);
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("W83L785TS-S driver");
MODULE_LICENSE("GPL");
module_init(sensors_w83l785ts_init);
module_exit(sensors_w83l785ts_exit);