2
0
mirror of https://github.com/lm-sensors/lm-sensors synced 2025-08-30 22:05:11 +00:00

GL518SM driver

To be done: support for GL518SM revision 0x80 and GL520SM

Kyösti, if you have some time soon, could you please test it?


git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@56 7894878c-1315-0410-8ee3-d5d059ff63e0
This commit is contained in:
Frodo Looijaard
1998-12-09 17:53:32 +00:00
parent 0e8799ff27
commit 5dd41b1046
10 changed files with 1332 additions and 15 deletions

70
doc/gl518sm Normal file
View File

@@ -0,0 +1,70 @@
This file documents the gl518sm directories.
There will be one directory created for each detected GL518SM chip. Chips
can only be connected to the SMBus. Directories are called gl518sm-i2c-?-??,
where the first question mark equals the I2C bus number, and the I2C
address is at the end.
/proc/sys/dev/sensors/chips contains the SYSCTL values for all chip
directories.
Within each LM78 directory, you can find the following files:
* alarms (GL518_SYSCTL_ALARMS)
A number which indicates which alarms are on. An alarm gets triggered when
some limit has been crossed. Even if the cause of the alarm is over, it
stays triggered until it has been read at least once. Because the LM78
values are updated at most once every 1.5 seconds, this means the alarm
can be read several times before it is erased.
This file can not be written to.
The number is the logical OR of the following components:
- GL518_ALARM_VDD
Gets triggered when the VDD value is higher or lower than its limits
- LM78_ALARM_VIN[1-3]
Gets triggered when the corresponding VIN value is higher or lower than
its limits.
- LM78_ALARM_TEMP
Gets triggered when the temp value has crossed its limits. See the
description under temp.
- LM78_ALARM_FAN[1-2]
Gets triggered when the corresponding FAN value drops below its limit.
If accessed through sysctl, this value is a long.
* fan[1-2] (GL518_SYSCTL_FAN[1-2])
A list of two numbers. The first is the minimum fan rotation limit; the
second is the current fan rotation speed. Both are in RPM (rotation per
minute). An alarm is triggered if the rotation speed drops below the
limit. The first value can be changed. Not all RPM values can accurately
be represented, so some rounding is done.
If accessed through sysctl, this is a list of longs.
* fan_div (GL518_SYSCTL_FAN_DIV)
A list of two numbers, one for each fan. Each number can be either 1, 2,
4 or 8. It is the internal scaling factor used for the FAN rotations. If
you change this number, the FAN readings get more or less detailed, but
the range in which they can be read too. Higher numbers give better
resolution, but less range. The first two numbers can be changed, the
third not.
If accessed through sysctl, this is a list of longs.
* vin[1-3] and vdd (LM78_SYSCTL_VIN[1-3] and GL518_SYSCTL_VDD)
A list of three numbers. The first is the minimum limit, the second is the
maximum limit, and the third is the current value. If you have a weird
mainboard, all values may be off because some other scaling factor has
to be used; user-space programs should compensate for this. Note that
minimum means 'closest to zero'; so if the normal value equals -10, a
minimum could equal -9, and a maximum -11.
On GL518SM revision 0 chips, only VIN3 is readable; for the others,
you can set limits, but you can not read the current values.
The first two numbers can be changed, the third not.
If accessed through sysctl, this is a list of longs, each being the voltage
times 100.
* temp
A list of three numbers. The first number is the Overtemperature Shutdown
value; the second is the Hysteris value and the third number is the
current value. The first two values can be modified. All values are in
degrees Celcius.
An alarm is issued when the temperature gets higher then the
Overtemperature Shutdown value; it stays on until the temperature falls
below the Hysteris value.
The first two numbers can be changed, the third not.
If accessed through sysctl, this is a list of longs, each being the
temperature times 10.
The data for each GL518SM is updated each 1.5 seconds, but only if it is
actually read.

View File

@@ -76,7 +76,8 @@ Within each LM78 directory, you can find the following files:
alarms are issued during all the time when the actual temperature is
above the Overtemperature Shutdown value.
The first two numbers can be changed, the third not.
If accessed through sysctl, this is a list of longs.
If accessed through sysctl, this is a list of longs, each being the
temperature in degrees Celcius times 10.
* vid
The core voltage value (the voltage level your processor should work with),
in volts. This is the value IN0 and IN1 are initialized to. If unconnected,

View File

@@ -24,7 +24,8 @@ MODULE_DIR := src
# defined value verbatim into the command-list of rules...
SRCTARGETS := $(MODULE_DIR)/smbus.o $(MODULE_DIR)/piix4.o $(MODULE_DIR)/isa.o \
$(MODULE_DIR)/lm78.o $(MODULE_DIR)/sensors.o \
$(MODULE_DIR)/i2c-proc.o $(MODULE_DIR)/lm75.o
$(MODULE_DIR)/i2c-proc.o $(MODULE_DIR)/lm75.o \
$(MODULE_DIR)/i2c-proc.o $(MODULE_DIR)/gl518sm.o
SRCHEADERFILES := $(MODULE_DIR)/sensors.h $(MODULE_DIR)/isa.h \
$(MODULE_DIR)/smbus.h

598
kernel/chips/gl518sm.c Normal file
View File

@@ -0,0 +1,598 @@
/*
gl518sm.c - A Linux module for reading sensor data.
Copyright (c) 1998 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/malloc.h>
#include "smbus.h"
#include "sensors.h"
#include "i2c.h"
#include "version.h"
/* 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 0x04
#define GL518_REG_TEMP_OVER 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_VOLT 0x0d
#define GL518_REG_MISC 0x0f
#define GL518_REG_ALARM 0x10
#define GL518_REG_MASK 0x11
#define GL518_REG_INT 0x12
/* Conversions. Rounding is only done on the TO_REG variants. */
#define TEMP_TO_REG(val) (((((val)<0?(val)-5:(val)+5) / 10) + 119) & 0xff)
#define TEMP_FROM_REG(val) (((val) - 119) * 10)
#define FAN_TO_REG(val,div) (((val)==0?255:\
(960000+((val)*(div)))/(2*(val)*(div))) & 0xff)
#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:960000/(2*(val)*(div)))
#define IN_TO_REG(val) ((((val)*10+8)/19) & 0xff)
#define IN_FROM_REG(val) (((val)*19)/10)
#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
#define DIV_FROM_REG(val) (1 << (val))
/* Initial values */
#define GL518_INIT_TEMP_OVER 600
#define GL518_INIT_TEMP_HYST 500
#define GL518_INIT_FAN_MIN_1 3000
#define GL518_INIT_FAN_MIN_2 3000
/* What are sane values for these?!? */
#define GL518_INIT_VIN_1 3000
#define GL518_INIT_VIN_2 3000
#define GL518_INIT_VIN_3 3000
#define GL518_INIT_VDD 3000
#define GL518_INIT_PERCENTAGE 10
#define GL518_INIT_VIN_MIN_1 \
(GL518_INIT_VIN_1 - GL518_INIT_VIN_1 * GL518_INIT_PERCENTAGE / 100)
#define GL518_INIT_VIN_MAX_1 \
(GL518_INIT_VIN_1 + GL518_INIT_VIN_1 * GL518_INIT_PERCENTAGE / 100)
#define GL518_INIT_VIN_MIN_2 \
(GL518_INIT_VIN_2 - GL518_INIT_VIN_2 * GL518_INIT_PERCENTAGE / 100)
#define GL518_INIT_VIN_MAX_2 \
(GL518_INIT_VIN_2 + GL518_INIT_VIN_2 * GL518_INIT_PERCENTAGE / 100)
#define GL518_INIT_VIN_MIN_3 \
(GL518_INIT_VIN_3 - GL518_INIT_VIN_3 * GL518_INIT_PERCENTAGE / 100)
#define GL518_INIT_VIN_MAX_3 \
(GL518_INIT_VIN_3 + GL518_INIT_VIN_3 * GL518_INIT_PERCENTAGE / 100)
#define GL518_INIT_VDD_MIN \
(GL518_INIT_VDD - GL518_INIT_VDD * GL518_INIT_PERCENTAGE / 100)
#define GL518_INIT_VDD_MAX \
(GL518_INIT_VDD + GL518_INIT_VDD * GL518_INIT_PERCENTAGE / 100)
/* Each client has this additional data */
struct gl518_data {
int sysctl_id;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u8 vin3; /* Register value */
u8 voltage_min[4]; /* Register values; [0] = VDD */
u8 voltage_max[4]; /* Register values; [0] = VDD */
u8 fan[2];
u8 fan_min[2];
u8 temp; /* Register values */
u8 temp_over; /* Register values */
u8 temp_hyst; /* Register values */
u8 alarms; /* Register value */
u8 fan_div[2]; /* Register encoding, shifted right */
};
#ifdef MODULE
extern int init_module(void);
extern int cleanup_module(void);
#endif /* MODULE */
static int gl518_init(void);
static int gl518_cleanup(void);
static int gl518_attach_adapter(struct i2c_adapter *adapter);
static int gl518_detach_client(struct i2c_client *client);
static int gl518_command(struct i2c_client *client, unsigned int cmd,
void *arg);
static void gl518_inc_use (struct i2c_client *client);
static void gl518_dec_use (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 void gl518_update_client(struct i2c_client *client);
static void gl518_vin(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void gl518_fan(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void gl518_temp(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void gl518_fan_div(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void gl518_alarms(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
/* This is the driver that will be inserted */
static struct i2c_driver gl518_driver = {
/* name */ "GL518SM sensor chip driver",
/* id */ I2C_DRIVERID_GL518,
/* flags */ DF_NOTIFY,
/* attach_adapter */ &gl518_attach_adapter,
/* detach_client */ &gl518_detach_client,
/* command */ &gl518_command,
/* inc_use */ &gl518_inc_use,
/* dec_use */ &gl518_dec_use
};
/* These files are created for each detected GL518. This is just a template;
though at first sight, you might think we could use a statically
allocated list, we need some way to get back to the parent - which
is done through one of the 'extra' fields which are initialized
when a new copy is allocated. */
static ctl_table gl518_dir_table_template[] = {
{ GL518_SYSCTL_VIN1, "vin1", NULL, 0, 0644, NULL, &sensors_proc_real,
&sensors_sysctl_real, NULL, &gl518_vin },
{ GL518_SYSCTL_VIN2, "vin2", NULL, 0, 0644, NULL, &sensors_proc_real,
&sensors_sysctl_real, NULL, &gl518_vin },
{ GL518_SYSCTL_VIN3, "vin3", NULL, 0, 0644, NULL, &sensors_proc_real,
&sensors_sysctl_real, NULL, &gl518_vin },
{ GL518_SYSCTL_VDD, "vdd", NULL, 0, 0644, NULL, &sensors_proc_real,
&sensors_sysctl_real, NULL, &gl518_vin },
{ GL518_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
&sensors_sysctl_real, NULL, &gl518_fan },
{ GL518_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
&sensors_sysctl_real, NULL, &gl518_fan },
{ GL518_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real,
&sensors_sysctl_real, NULL, &gl518_temp },
{ GL518_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
&sensors_sysctl_real, NULL, &gl518_fan_div },
{ GL518_SYSCTL_ALARMS, "alarms", NULL, 0, 0644, NULL, &sensors_proc_real,
&sensors_sysctl_real, NULL, &gl518_alarms },
{ 0 }
};
/* Used by init/cleanup */
static int gl518_initialized = 0;
/* I choose here for semi-static LM78 allocation. Complete dynamic
allocation could also be used; the code needed for this would probably
take more memory than the datastructure takes now. */
#define MAX_GL518_NR 4
static struct i2c_client *gl518_list[MAX_GL518_NR];
int gl518_attach_adapter(struct i2c_adapter *adapter)
{
int address,err,i;
struct i2c_client *new_client;
struct gl518_data *data;
err = 0;
/* OK, this is no detection. I know. It will do for now, though. */
/* Set err only if a global error would make registering other clients
impossible too (like out-of-memory). */
for (address = 0x2c; (! err) && (address <= 0x2d); address ++) {
/* Later on, we will keep a list of registered addresses for each
adapter, and check whether they are used here */
if ((i = smbus_read_byte_data(adapter,address,GL518_REG_CHIP_ID)) < 0)
continue;
if (i != 0x80)
continue;
/* Real detection code goes here */
/* Allocate space for a new client structure */
if (! (new_client = kmalloc(sizeof(struct i2c_client) +
sizeof(struct gl518_data),
GFP_KERNEL))) {
err = -ENOMEM;
continue;
}
/* Find a place in our global list */
for (i = 0; i < MAX_GL518_NR; i++)
if (! gl518_list[i])
break;
if (i == MAX_GL518_NR) {
err = -ENOMEM;
printk("gl518sm.o: No empty slots left, recompile and heighten "
"MAX_GL518_NR!\n");
goto ERROR1;
}
gl518_list[i] = new_client;
/* Fill the new client structure with data */
data = (struct gl518_data *) (new_client + 1);
new_client->data = data;
new_client->id = i;
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &gl518_driver;
strcpy(new_client->name,"GL518SM chip");
data->valid = 0;
data->update_lock = MUTEX;
/* Tell i2c-core a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto ERROR2;
/* Register a new directory entry with module sensors */
if ((err = sensors_register_entry(new_client,"gl518",
gl518_dir_table_template)) < 0)
goto ERROR3;
data->sysctl_id = err;
err = 0;
/* Initialize the GL518SM chip */
/* Power-on defaults (bit 7=1) */
gl518_write_value(new_client,GL518_REG_CONF,0x80);
/* No noisy output (bit 2=1), Comparator mode (bit 3=0), two fans (bit4=0),
standby mode (bit6=0) */
gl518_write_value(new_client,GL518_REG_CONF,0x04);
gl518_write_value(new_client,GL518_REG_TEMP_HYST,
TEMP_TO_REG(GL518_INIT_TEMP_HYST));
gl518_write_value(new_client,GL518_REG_TEMP_OVER,
TEMP_TO_REG(GL518_INIT_TEMP_OVER));
gl518_write_value(new_client,GL518_REG_MISC,(DIV_TO_REG(2) << 6) |
(DIV_TO_REG(2) << 4) | 0x08);
gl518_write_value(new_client,GL518_REG_FAN_LIMIT,
(FAN_TO_REG(GL518_INIT_FAN_MIN_1,2) << 8) |
FAN_TO_REG(GL518_INIT_FAN_MIN_2,2));
gl518_write_value(new_client,GL518_REG_VIN1_LIMIT,
(IN_TO_REG(GL518_INIT_VIN_MIN_1) << 8) |
IN_TO_REG(GL518_INIT_VIN_MAX_1));
gl518_write_value(new_client,GL518_REG_VIN2_LIMIT,
(IN_TO_REG(GL518_INIT_VIN_MIN_2) << 8) |
IN_TO_REG(GL518_INIT_VIN_MAX_2));
gl518_write_value(new_client,GL518_REG_VIN3_LIMIT,
(IN_TO_REG(GL518_INIT_VIN_MIN_3) << 8) |
IN_TO_REG(GL518_INIT_VIN_MAX_3));
gl518_write_value(new_client,GL518_REG_VDD_LIMIT,
(IN_TO_REG(GL518_INIT_VDD_MIN) << 8) |
IN_TO_REG(GL518_INIT_VDD_MAX));
/* Clear status register (bit 5=1), start (bit6=1) */
gl518_write_value(new_client,GL518_REG_CONF,0x64);
continue;
/* OK, this is not exactly good programming practice, usually. But it is
very code-efficient in this case. */
ERROR3:
i2c_detach_client(new_client);
ERROR2:
gl518_list[i] = NULL;
ERROR1:
kfree(new_client);
}
return err;
}
int gl518_detach_client(struct i2c_client *client)
{
int err,i;
for (i = 0; i < MAX_GL518_NR; i++)
if (client == gl518_list[i])
break;
if ((i == MAX_GL518_NR)) {
printk("gl518sm.o: Client to detach not found.\n");
return -ENOENT;
}
sensors_deregister_entry(((struct gl518_data *)(client->data))->sysctl_id);
if ((err = i2c_detach_client(client))) {
printk("gl518sm.o: Client deregistration failed, client not detached.\n");
return err;
}
gl518_list[i] = NULL;
kfree(client);
return 0;
}
/* No commands defined yet */
int gl518_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
return 0;
}
/* Nothing here yet */
void gl518_inc_use (struct i2c_client *client)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
/* Nothing here yet */
void gl518_dec_use (struct i2c_client *client)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
/* Registers 0x07 to 0x0c are word-sized, others are byte-sized */
int gl518_read_value(struct i2c_client *client, u8 reg)
{
if ((reg >= 0x07) && (reg <= 0x0c))
return smbus_read_word_data(client->adapter,client->addr,reg);
else
return smbus_read_byte_data(client->adapter,client->addr,reg);
}
int gl518_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if ((reg >= 0x07) && (reg <= 0x0c))
return smbus_write_word_data(client->adapter,client->addr,reg,value);
else
return smbus_write_byte_data(client->adapter,client->addr,reg,value);
}
void gl518_update_client(struct i2c_client *client)
{
struct gl518_data *data = client->data;
int val;
down(&data->update_lock);
if ((jiffies - data->last_updated > HZ+HZ/2 ) ||
(jiffies < data->last_updated) || ! data->valid) {
#ifdef DEBUG
printk("Starting gl518 update\n");
#endif
data->vin3 = gl518_read_value(client,GL518_REG_VOLT);
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[0] = (val >> 8) & 0xff;
data->fan[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 = gl518_read_value(client,GL518_REG_TEMP);
data->temp_over = gl518_read_value(client,GL518_REG_TEMP_OVER);
data->temp_hyst = gl518_read_value(client,GL518_REG_TEMP_HYST);
data->alarms = gl518_read_value(client,GL518_REG_ALARM);
val = gl518_read_value(client,GL518_REG_MISC);
data->fan_div[0] = (val >> 4) & 0x03;
data->fan_div[1] = (val >> 6) & 0x03;
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
}
void gl518_temp(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct gl518_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 1;
else if (operation == SENSORS_PROC_REAL_READ) {
gl518_update_client(client);
results[0] = TEMP_FROM_REG(data->temp_over);
results[1] = TEMP_FROM_REG(data->temp_hyst);
results[2] = TEMP_FROM_REG(data->temp);
*nrels_mag = 3;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
data->temp_over = TEMP_TO_REG(results[0]);
gl518_write_value(client,GL518_REG_TEMP_OVER,data->temp_over);
}
if (*nrels_mag >= 2) {
data->temp_over = TEMP_TO_REG(results[1]);
gl518_write_value(client,GL518_REG_TEMP_HYST,data->temp_over);
}
}
}
void gl518_vin(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct gl518_data *data = client->data;
int nr = ctl_name - GL518_SYSCTL_VDD;
int regnr,old=0;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 2;
else if (operation == SENSORS_PROC_REAL_READ) {
gl518_update_client(client);
results[0] = IN_FROM_REG(data->voltage_min[nr]);
results[1] = IN_FROM_REG(data->voltage_max[nr]);
if (nr == 3)
results[2] = IN_FROM_REG(data->vin3);
else
results[2] = 0;
*nrels_mag = 3;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
regnr=nr==0?GL518_REG_VDD_LIMIT:nr==1?GL518_REG_VIN1_LIMIT:nr==2?
GL518_REG_VIN2_LIMIT:GL518_REG_VIN3_LIMIT;
if (*nrels_mag == 1)
old = gl518_read_value(client,regnr) & 0xff00;
if (*nrels_mag >= 2) {
data->voltage_max[nr] = IN_TO_REG(results[1]);
old = data->voltage_max[nr] << 8;
}
if (*nrels_mag >= 1) {
data->voltage_min[nr] = IN_TO_REG(results[0]);
old |= data->voltage_min[nr];
gl518_write_value(client,regnr,old);
}
}
}
void gl518_fan(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct gl518_data *data = client->data;
int nr = ctl_name - GL518_SYSCTL_FAN1;
int old;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
gl518_update_client(client);
results[0] = FAN_FROM_REG(data->fan_min[nr],data->fan_div[nr]);
results[1] = FAN_FROM_REG(data->fan[nr],data->fan_div[nr]);
*nrels_mag = 2;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
data->fan_min[nr] = FAN_TO_REG(results[0],data->fan_div[nr]);
old = gl518_read_value(client,GL518_REG_FAN_LIMIT);
if (nr == 0)
old = (old & 0x00ff) | (data->fan_min[nr] << 8);
else
old = (old & 0xff00) | data->fan_min[nr];
gl518_write_value(client,GL518_REG_FAN_LIMIT,old);
}
}
}
void gl518_alarms(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct gl518_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
gl518_update_client(client);
results[0] = data->alarms;
*nrels_mag = 1;
}
}
void gl518_fan_div(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct gl518_data *data = client->data;
int old;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
gl518_update_client(client);
results[0] = DIV_FROM_REG(data->fan_div[0]);
results[1] = DIV_FROM_REG(data->fan_div[1]);
*nrels_mag = 2;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
old = gl518_read_value(client,GL518_REG_MISC);
if (*nrels_mag >= 2) {
data->fan_div[1] = DIV_TO_REG(results[1]);
old = (old & 0x3f) | (data->fan_div[1] << 6);
}
if (*nrels_mag >= 1) {
data->fan_div[1] = DIV_TO_REG(results[0]);
old = (old & 0xcf) | (data->fan_div[0] << 4);
gl518_write_value(client,GL518_REG_MISC,old);
}
}
}
int gl518_init(void)
{
int res;
printk("gl518sm.o version %s (%s)\n",LM_VERSION,LM_DATE);
gl518_initialized = 0;
if ((res = i2c_add_driver(&gl518_driver))) {
printk("gl518sm.o: Driver registration failed, module not inserted.\n");
gl518_cleanup();
return res;
}
gl518_initialized ++;
return 0;
}
int gl518_cleanup(void)
{
int res;
if (gl518_initialized >= 1) {
if ((res = i2c_del_driver(&gl518_driver))) {
printk("gl518.o: Driver deregistration failed, module not removed.\n");
return res;
}
gl518_initialized --;
}
return 0;
}
#ifdef MODULE
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
MODULE_DESCRIPTION("GL518SM driver");
int init_module(void)
{
return gl518_init();
}
int cleanup_module(void)
{
return gl518_cleanup();
}
#endif /* MODULE */

View File

@@ -751,9 +751,9 @@ void lm78_in(struct i2c_client *client, int operation, int ctl_name,
lm78_write_value(client,LM78_REG_IN_MIN(nr),data->in_min[nr]);
}
if (*nrels_mag >= 2) {
data->in_max[nr] = IN_TO_REG(results[1],nr);
lm78_write_value(client,LM78_REG_IN_MAX(nr),data->in_max[nr]);
}
data->in_max[nr] = IN_TO_REG(results[1],nr);
lm78_write_value(client,LM78_REG_IN_MAX(nr),data->in_max[nr]);
}
}
}
@@ -833,6 +833,8 @@ void lm78_fan_div(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct lm78_data *data = client->data;
int old;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
@@ -842,12 +844,15 @@ void lm78_fan_div(struct i2c_client *client, int operation, int ctl_name,
results[2] = 2;
*nrels_mag = 3;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 2)
old = lm78_read_value(client,LM78_REG_VID_FANDIV);
if (*nrels_mag >= 2) {
data->fan_div[1] = DIV_TO_REG(results[1]);
old = (old & 0x3f) | (data->fan_div[1] << 6);
}
if (*nrels_mag >= 1) {
data->fan_div[0] = DIV_TO_REG(results[0]);
lm78_write_value(client,LM78_REG_VID_FANDIV,
(data->fan_div[0] >> 4) | (data->fan_div[1] >> 6));
old = (old & 0xcf) | (data->fan_div[0] << 4);
lm78_write_value(client,LM78_REG_VID_FANDIV,old);
}
}
}

View File

@@ -83,6 +83,7 @@ extern void sensors_deregister_entry(int id);
#define I2C_DRIVERID_I2CPROC 1001
#define I2C_DRIVERID_LM78 1002
#define I2C_DRIVERID_LM75 1003
#define I2C_DRIVERID_GL518 1004
/* Sysctl IDs */
#ifdef DEV_HWMON
@@ -131,4 +132,22 @@ struct sensors_chips_data {
#define LM75_SYSCTL_TEMP 1200 /* Degrees Celcius * 10 */
#define GL518_SYSCTL_VDD 1000 /* Volts * 100 */
#define GL518_SYSCTL_VIN1 1001
#define GL518_SYSCTL_VIN2 1002
#define GL518_SYSCTL_VIN3 1003
#define GL518_SYSCTL_FAN1 1101 /* RPM */
#define GL518_SYSCTL_FAN2 1102
#define GL518_SYSCTL_TEMP 1200 /* Degrees Celcius * 10 */
#define GL518_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */
#define GL518_SYSCTL_ALARMS 2001 /* bitvector */
#define GL518_ALARM_VDD 0x01
#define GL518_ALARM_VIN1 0x02
#define GL518_ALARM_VIN2 0x04
#define GL518_ALARM_VIN3 0x08
#define GL518_ALARM_TEMP 0x10
#define GL518_ALARM_FAN1 0x20
#define GL518_ALARM_FAN2 0x40
#endif /* def SENSORS_SENSORS_H */

View File

@@ -24,7 +24,8 @@ MODULE_DIR := src
# defined value verbatim into the command-list of rules...
SRCTARGETS := $(MODULE_DIR)/smbus.o $(MODULE_DIR)/piix4.o $(MODULE_DIR)/isa.o \
$(MODULE_DIR)/lm78.o $(MODULE_DIR)/sensors.o \
$(MODULE_DIR)/i2c-proc.o $(MODULE_DIR)/lm75.o
$(MODULE_DIR)/i2c-proc.o $(MODULE_DIR)/lm75.o \
$(MODULE_DIR)/i2c-proc.o $(MODULE_DIR)/gl518sm.o
SRCHEADERFILES := $(MODULE_DIR)/sensors.h $(MODULE_DIR)/isa.h \
$(MODULE_DIR)/smbus.h

598
src/gl518sm.c Normal file
View File

@@ -0,0 +1,598 @@
/*
gl518sm.c - A Linux module for reading sensor data.
Copyright (c) 1998 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/malloc.h>
#include "smbus.h"
#include "sensors.h"
#include "i2c.h"
#include "version.h"
/* 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 0x04
#define GL518_REG_TEMP_OVER 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_VOLT 0x0d
#define GL518_REG_MISC 0x0f
#define GL518_REG_ALARM 0x10
#define GL518_REG_MASK 0x11
#define GL518_REG_INT 0x12
/* Conversions. Rounding is only done on the TO_REG variants. */
#define TEMP_TO_REG(val) (((((val)<0?(val)-5:(val)+5) / 10) + 119) & 0xff)
#define TEMP_FROM_REG(val) (((val) - 119) * 10)
#define FAN_TO_REG(val,div) (((val)==0?255:\
(960000+((val)*(div)))/(2*(val)*(div))) & 0xff)
#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:960000/(2*(val)*(div)))
#define IN_TO_REG(val) ((((val)*10+8)/19) & 0xff)
#define IN_FROM_REG(val) (((val)*19)/10)
#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
#define DIV_FROM_REG(val) (1 << (val))
/* Initial values */
#define GL518_INIT_TEMP_OVER 600
#define GL518_INIT_TEMP_HYST 500
#define GL518_INIT_FAN_MIN_1 3000
#define GL518_INIT_FAN_MIN_2 3000
/* What are sane values for these?!? */
#define GL518_INIT_VIN_1 3000
#define GL518_INIT_VIN_2 3000
#define GL518_INIT_VIN_3 3000
#define GL518_INIT_VDD 3000
#define GL518_INIT_PERCENTAGE 10
#define GL518_INIT_VIN_MIN_1 \
(GL518_INIT_VIN_1 - GL518_INIT_VIN_1 * GL518_INIT_PERCENTAGE / 100)
#define GL518_INIT_VIN_MAX_1 \
(GL518_INIT_VIN_1 + GL518_INIT_VIN_1 * GL518_INIT_PERCENTAGE / 100)
#define GL518_INIT_VIN_MIN_2 \
(GL518_INIT_VIN_2 - GL518_INIT_VIN_2 * GL518_INIT_PERCENTAGE / 100)
#define GL518_INIT_VIN_MAX_2 \
(GL518_INIT_VIN_2 + GL518_INIT_VIN_2 * GL518_INIT_PERCENTAGE / 100)
#define GL518_INIT_VIN_MIN_3 \
(GL518_INIT_VIN_3 - GL518_INIT_VIN_3 * GL518_INIT_PERCENTAGE / 100)
#define GL518_INIT_VIN_MAX_3 \
(GL518_INIT_VIN_3 + GL518_INIT_VIN_3 * GL518_INIT_PERCENTAGE / 100)
#define GL518_INIT_VDD_MIN \
(GL518_INIT_VDD - GL518_INIT_VDD * GL518_INIT_PERCENTAGE / 100)
#define GL518_INIT_VDD_MAX \
(GL518_INIT_VDD + GL518_INIT_VDD * GL518_INIT_PERCENTAGE / 100)
/* Each client has this additional data */
struct gl518_data {
int sysctl_id;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u8 vin3; /* Register value */
u8 voltage_min[4]; /* Register values; [0] = VDD */
u8 voltage_max[4]; /* Register values; [0] = VDD */
u8 fan[2];
u8 fan_min[2];
u8 temp; /* Register values */
u8 temp_over; /* Register values */
u8 temp_hyst; /* Register values */
u8 alarms; /* Register value */
u8 fan_div[2]; /* Register encoding, shifted right */
};
#ifdef MODULE
extern int init_module(void);
extern int cleanup_module(void);
#endif /* MODULE */
static int gl518_init(void);
static int gl518_cleanup(void);
static int gl518_attach_adapter(struct i2c_adapter *adapter);
static int gl518_detach_client(struct i2c_client *client);
static int gl518_command(struct i2c_client *client, unsigned int cmd,
void *arg);
static void gl518_inc_use (struct i2c_client *client);
static void gl518_dec_use (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 void gl518_update_client(struct i2c_client *client);
static void gl518_vin(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void gl518_fan(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void gl518_temp(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void gl518_fan_div(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void gl518_alarms(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
/* This is the driver that will be inserted */
static struct i2c_driver gl518_driver = {
/* name */ "GL518SM sensor chip driver",
/* id */ I2C_DRIVERID_GL518,
/* flags */ DF_NOTIFY,
/* attach_adapter */ &gl518_attach_adapter,
/* detach_client */ &gl518_detach_client,
/* command */ &gl518_command,
/* inc_use */ &gl518_inc_use,
/* dec_use */ &gl518_dec_use
};
/* These files are created for each detected GL518. This is just a template;
though at first sight, you might think we could use a statically
allocated list, we need some way to get back to the parent - which
is done through one of the 'extra' fields which are initialized
when a new copy is allocated. */
static ctl_table gl518_dir_table_template[] = {
{ GL518_SYSCTL_VIN1, "vin1", NULL, 0, 0644, NULL, &sensors_proc_real,
&sensors_sysctl_real, NULL, &gl518_vin },
{ GL518_SYSCTL_VIN2, "vin2", NULL, 0, 0644, NULL, &sensors_proc_real,
&sensors_sysctl_real, NULL, &gl518_vin },
{ GL518_SYSCTL_VIN3, "vin3", NULL, 0, 0644, NULL, &sensors_proc_real,
&sensors_sysctl_real, NULL, &gl518_vin },
{ GL518_SYSCTL_VDD, "vdd", NULL, 0, 0644, NULL, &sensors_proc_real,
&sensors_sysctl_real, NULL, &gl518_vin },
{ GL518_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
&sensors_sysctl_real, NULL, &gl518_fan },
{ GL518_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
&sensors_sysctl_real, NULL, &gl518_fan },
{ GL518_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real,
&sensors_sysctl_real, NULL, &gl518_temp },
{ GL518_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
&sensors_sysctl_real, NULL, &gl518_fan_div },
{ GL518_SYSCTL_ALARMS, "alarms", NULL, 0, 0644, NULL, &sensors_proc_real,
&sensors_sysctl_real, NULL, &gl518_alarms },
{ 0 }
};
/* Used by init/cleanup */
static int gl518_initialized = 0;
/* I choose here for semi-static LM78 allocation. Complete dynamic
allocation could also be used; the code needed for this would probably
take more memory than the datastructure takes now. */
#define MAX_GL518_NR 4
static struct i2c_client *gl518_list[MAX_GL518_NR];
int gl518_attach_adapter(struct i2c_adapter *adapter)
{
int address,err,i;
struct i2c_client *new_client;
struct gl518_data *data;
err = 0;
/* OK, this is no detection. I know. It will do for now, though. */
/* Set err only if a global error would make registering other clients
impossible too (like out-of-memory). */
for (address = 0x2c; (! err) && (address <= 0x2d); address ++) {
/* Later on, we will keep a list of registered addresses for each
adapter, and check whether they are used here */
if ((i = smbus_read_byte_data(adapter,address,GL518_REG_CHIP_ID)) < 0)
continue;
if (i != 0x80)
continue;
/* Real detection code goes here */
/* Allocate space for a new client structure */
if (! (new_client = kmalloc(sizeof(struct i2c_client) +
sizeof(struct gl518_data),
GFP_KERNEL))) {
err = -ENOMEM;
continue;
}
/* Find a place in our global list */
for (i = 0; i < MAX_GL518_NR; i++)
if (! gl518_list[i])
break;
if (i == MAX_GL518_NR) {
err = -ENOMEM;
printk("gl518sm.o: No empty slots left, recompile and heighten "
"MAX_GL518_NR!\n");
goto ERROR1;
}
gl518_list[i] = new_client;
/* Fill the new client structure with data */
data = (struct gl518_data *) (new_client + 1);
new_client->data = data;
new_client->id = i;
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &gl518_driver;
strcpy(new_client->name,"GL518SM chip");
data->valid = 0;
data->update_lock = MUTEX;
/* Tell i2c-core a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto ERROR2;
/* Register a new directory entry with module sensors */
if ((err = sensors_register_entry(new_client,"gl518",
gl518_dir_table_template)) < 0)
goto ERROR3;
data->sysctl_id = err;
err = 0;
/* Initialize the GL518SM chip */
/* Power-on defaults (bit 7=1) */
gl518_write_value(new_client,GL518_REG_CONF,0x80);
/* No noisy output (bit 2=1), Comparator mode (bit 3=0), two fans (bit4=0),
standby mode (bit6=0) */
gl518_write_value(new_client,GL518_REG_CONF,0x04);
gl518_write_value(new_client,GL518_REG_TEMP_HYST,
TEMP_TO_REG(GL518_INIT_TEMP_HYST));
gl518_write_value(new_client,GL518_REG_TEMP_OVER,
TEMP_TO_REG(GL518_INIT_TEMP_OVER));
gl518_write_value(new_client,GL518_REG_MISC,(DIV_TO_REG(2) << 6) |
(DIV_TO_REG(2) << 4) | 0x08);
gl518_write_value(new_client,GL518_REG_FAN_LIMIT,
(FAN_TO_REG(GL518_INIT_FAN_MIN_1,2) << 8) |
FAN_TO_REG(GL518_INIT_FAN_MIN_2,2));
gl518_write_value(new_client,GL518_REG_VIN1_LIMIT,
(IN_TO_REG(GL518_INIT_VIN_MIN_1) << 8) |
IN_TO_REG(GL518_INIT_VIN_MAX_1));
gl518_write_value(new_client,GL518_REG_VIN2_LIMIT,
(IN_TO_REG(GL518_INIT_VIN_MIN_2) << 8) |
IN_TO_REG(GL518_INIT_VIN_MAX_2));
gl518_write_value(new_client,GL518_REG_VIN3_LIMIT,
(IN_TO_REG(GL518_INIT_VIN_MIN_3) << 8) |
IN_TO_REG(GL518_INIT_VIN_MAX_3));
gl518_write_value(new_client,GL518_REG_VDD_LIMIT,
(IN_TO_REG(GL518_INIT_VDD_MIN) << 8) |
IN_TO_REG(GL518_INIT_VDD_MAX));
/* Clear status register (bit 5=1), start (bit6=1) */
gl518_write_value(new_client,GL518_REG_CONF,0x64);
continue;
/* OK, this is not exactly good programming practice, usually. But it is
very code-efficient in this case. */
ERROR3:
i2c_detach_client(new_client);
ERROR2:
gl518_list[i] = NULL;
ERROR1:
kfree(new_client);
}
return err;
}
int gl518_detach_client(struct i2c_client *client)
{
int err,i;
for (i = 0; i < MAX_GL518_NR; i++)
if (client == gl518_list[i])
break;
if ((i == MAX_GL518_NR)) {
printk("gl518sm.o: Client to detach not found.\n");
return -ENOENT;
}
sensors_deregister_entry(((struct gl518_data *)(client->data))->sysctl_id);
if ((err = i2c_detach_client(client))) {
printk("gl518sm.o: Client deregistration failed, client not detached.\n");
return err;
}
gl518_list[i] = NULL;
kfree(client);
return 0;
}
/* No commands defined yet */
int gl518_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
return 0;
}
/* Nothing here yet */
void gl518_inc_use (struct i2c_client *client)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
/* Nothing here yet */
void gl518_dec_use (struct i2c_client *client)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
/* Registers 0x07 to 0x0c are word-sized, others are byte-sized */
int gl518_read_value(struct i2c_client *client, u8 reg)
{
if ((reg >= 0x07) && (reg <= 0x0c))
return smbus_read_word_data(client->adapter,client->addr,reg);
else
return smbus_read_byte_data(client->adapter,client->addr,reg);
}
int gl518_write_value(struct i2c_client *client, u8 reg, u16 value)
{
if ((reg >= 0x07) && (reg <= 0x0c))
return smbus_write_word_data(client->adapter,client->addr,reg,value);
else
return smbus_write_byte_data(client->adapter,client->addr,reg,value);
}
void gl518_update_client(struct i2c_client *client)
{
struct gl518_data *data = client->data;
int val;
down(&data->update_lock);
if ((jiffies - data->last_updated > HZ+HZ/2 ) ||
(jiffies < data->last_updated) || ! data->valid) {
#ifdef DEBUG
printk("Starting gl518 update\n");
#endif
data->vin3 = gl518_read_value(client,GL518_REG_VOLT);
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[0] = (val >> 8) & 0xff;
data->fan[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 = gl518_read_value(client,GL518_REG_TEMP);
data->temp_over = gl518_read_value(client,GL518_REG_TEMP_OVER);
data->temp_hyst = gl518_read_value(client,GL518_REG_TEMP_HYST);
data->alarms = gl518_read_value(client,GL518_REG_ALARM);
val = gl518_read_value(client,GL518_REG_MISC);
data->fan_div[0] = (val >> 4) & 0x03;
data->fan_div[1] = (val >> 6) & 0x03;
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
}
void gl518_temp(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct gl518_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 1;
else if (operation == SENSORS_PROC_REAL_READ) {
gl518_update_client(client);
results[0] = TEMP_FROM_REG(data->temp_over);
results[1] = TEMP_FROM_REG(data->temp_hyst);
results[2] = TEMP_FROM_REG(data->temp);
*nrels_mag = 3;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
data->temp_over = TEMP_TO_REG(results[0]);
gl518_write_value(client,GL518_REG_TEMP_OVER,data->temp_over);
}
if (*nrels_mag >= 2) {
data->temp_over = TEMP_TO_REG(results[1]);
gl518_write_value(client,GL518_REG_TEMP_HYST,data->temp_over);
}
}
}
void gl518_vin(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct gl518_data *data = client->data;
int nr = ctl_name - GL518_SYSCTL_VDD;
int regnr,old=0;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 2;
else if (operation == SENSORS_PROC_REAL_READ) {
gl518_update_client(client);
results[0] = IN_FROM_REG(data->voltage_min[nr]);
results[1] = IN_FROM_REG(data->voltage_max[nr]);
if (nr == 3)
results[2] = IN_FROM_REG(data->vin3);
else
results[2] = 0;
*nrels_mag = 3;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
regnr=nr==0?GL518_REG_VDD_LIMIT:nr==1?GL518_REG_VIN1_LIMIT:nr==2?
GL518_REG_VIN2_LIMIT:GL518_REG_VIN3_LIMIT;
if (*nrels_mag == 1)
old = gl518_read_value(client,regnr) & 0xff00;
if (*nrels_mag >= 2) {
data->voltage_max[nr] = IN_TO_REG(results[1]);
old = data->voltage_max[nr] << 8;
}
if (*nrels_mag >= 1) {
data->voltage_min[nr] = IN_TO_REG(results[0]);
old |= data->voltage_min[nr];
gl518_write_value(client,regnr,old);
}
}
}
void gl518_fan(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct gl518_data *data = client->data;
int nr = ctl_name - GL518_SYSCTL_FAN1;
int old;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
gl518_update_client(client);
results[0] = FAN_FROM_REG(data->fan_min[nr],data->fan_div[nr]);
results[1] = FAN_FROM_REG(data->fan[nr],data->fan_div[nr]);
*nrels_mag = 2;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
data->fan_min[nr] = FAN_TO_REG(results[0],data->fan_div[nr]);
old = gl518_read_value(client,GL518_REG_FAN_LIMIT);
if (nr == 0)
old = (old & 0x00ff) | (data->fan_min[nr] << 8);
else
old = (old & 0xff00) | data->fan_min[nr];
gl518_write_value(client,GL518_REG_FAN_LIMIT,old);
}
}
}
void gl518_alarms(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct gl518_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
gl518_update_client(client);
results[0] = data->alarms;
*nrels_mag = 1;
}
}
void gl518_fan_div(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct gl518_data *data = client->data;
int old;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
gl518_update_client(client);
results[0] = DIV_FROM_REG(data->fan_div[0]);
results[1] = DIV_FROM_REG(data->fan_div[1]);
*nrels_mag = 2;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
old = gl518_read_value(client,GL518_REG_MISC);
if (*nrels_mag >= 2) {
data->fan_div[1] = DIV_TO_REG(results[1]);
old = (old & 0x3f) | (data->fan_div[1] << 6);
}
if (*nrels_mag >= 1) {
data->fan_div[1] = DIV_TO_REG(results[0]);
old = (old & 0xcf) | (data->fan_div[0] << 4);
gl518_write_value(client,GL518_REG_MISC,old);
}
}
}
int gl518_init(void)
{
int res;
printk("gl518sm.o version %s (%s)\n",LM_VERSION,LM_DATE);
gl518_initialized = 0;
if ((res = i2c_add_driver(&gl518_driver))) {
printk("gl518sm.o: Driver registration failed, module not inserted.\n");
gl518_cleanup();
return res;
}
gl518_initialized ++;
return 0;
}
int gl518_cleanup(void)
{
int res;
if (gl518_initialized >= 1) {
if ((res = i2c_del_driver(&gl518_driver))) {
printk("gl518.o: Driver deregistration failed, module not removed.\n");
return res;
}
gl518_initialized --;
}
return 0;
}
#ifdef MODULE
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
MODULE_DESCRIPTION("GL518SM driver");
int init_module(void)
{
return gl518_init();
}
int cleanup_module(void)
{
return gl518_cleanup();
}
#endif /* MODULE */

View File

@@ -751,9 +751,9 @@ void lm78_in(struct i2c_client *client, int operation, int ctl_name,
lm78_write_value(client,LM78_REG_IN_MIN(nr),data->in_min[nr]);
}
if (*nrels_mag >= 2) {
data->in_max[nr] = IN_TO_REG(results[1],nr);
lm78_write_value(client,LM78_REG_IN_MAX(nr),data->in_max[nr]);
}
data->in_max[nr] = IN_TO_REG(results[1],nr);
lm78_write_value(client,LM78_REG_IN_MAX(nr),data->in_max[nr]);
}
}
}
@@ -833,6 +833,8 @@ void lm78_fan_div(struct i2c_client *client, int operation, int ctl_name,
int *nrels_mag, long *results)
{
struct lm78_data *data = client->data;
int old;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
@@ -842,12 +844,15 @@ void lm78_fan_div(struct i2c_client *client, int operation, int ctl_name,
results[2] = 2;
*nrels_mag = 3;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 2)
old = lm78_read_value(client,LM78_REG_VID_FANDIV);
if (*nrels_mag >= 2) {
data->fan_div[1] = DIV_TO_REG(results[1]);
old = (old & 0x3f) | (data->fan_div[1] << 6);
}
if (*nrels_mag >= 1) {
data->fan_div[0] = DIV_TO_REG(results[0]);
lm78_write_value(client,LM78_REG_VID_FANDIV,
(data->fan_div[0] >> 4) | (data->fan_div[1] >> 6));
old = (old & 0xcf) | (data->fan_div[0] << 4);
lm78_write_value(client,LM78_REG_VID_FANDIV,old);
}
}
}

View File

@@ -83,6 +83,7 @@ extern void sensors_deregister_entry(int id);
#define I2C_DRIVERID_I2CPROC 1001
#define I2C_DRIVERID_LM78 1002
#define I2C_DRIVERID_LM75 1003
#define I2C_DRIVERID_GL518 1004
/* Sysctl IDs */
#ifdef DEV_HWMON
@@ -131,4 +132,22 @@ struct sensors_chips_data {
#define LM75_SYSCTL_TEMP 1200 /* Degrees Celcius * 10 */
#define GL518_SYSCTL_VDD 1000 /* Volts * 100 */
#define GL518_SYSCTL_VIN1 1001
#define GL518_SYSCTL_VIN2 1002
#define GL518_SYSCTL_VIN3 1003
#define GL518_SYSCTL_FAN1 1101 /* RPM */
#define GL518_SYSCTL_FAN2 1102
#define GL518_SYSCTL_TEMP 1200 /* Degrees Celcius * 10 */
#define GL518_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */
#define GL518_SYSCTL_ALARMS 2001 /* bitvector */
#define GL518_ALARM_VDD 0x01
#define GL518_ALARM_VIN1 0x02
#define GL518_ALARM_VIN2 0x04
#define GL518_ALARM_VIN3 0x08
#define GL518_ALARM_TEMP 0x10
#define GL518_ALARM_FAN1 0x20
#define GL518_ALARM_FAN2 0x40
#endif /* def SENSORS_SENSORS_H */