mirror of
https://github.com/lm-sensors/lm-sensors.git
synced 2026-11-01 13:20:30 +00:00
(Phil) Oh boy, here we go! :') This is the removal of the original files
found under lm_sensors2/src and moved to lm_sensors/kernel (and new
sub-directories), as suggested by Frodo (from his email):
kernel (i2c-dev.c, i2c-proc.c, sensors.c, smbus.c, compat.h)
|
|-- include (smbus.h, sensors.h, i2c-dev.h, isa.h)
|
|-- busses (i2c-isa.c, i2c-piix4.c, i2c-via.c)
|
|-- chips (adm1021.c, adm9240.c, eeprom.c, gl518sm.c, lm75.c, lm78.c,
lm80.c, ltc1710.c, w83781d.c)
There may need to be more shuffling of a few more files, but this is the
bulk of the movement. Of course, none of this will compile without some
changes to the appropriate 'make' files.
Also note that this commit notes only the removal and not the insertion
of the copies because that insertions were done by hand on the CVS
repository its self.
Last note, the change history (and comment logs) are intact, but the tags
were removed on the new files (because the files didn't really exist when
the tags were first made).
Don't forget to do a 'cvs update -d', and to also remove lm_sensors2/src!
git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@175 7894878c-1315-0410-8ee3-d5d059ff63e0
This commit is contained in:
1 parent
eeddf2c5c7
commit
5d35c0d282
22 files changed
-9360
No files matched your search
@@ -1,51 +0,0 @@
|
||||
# Module.mk - Makefile for 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.
|
||||
|
||||
# Note that MODULE_DIR (the directory in which this file resides) is a
|
||||
# 'simply expanded variable'. That means that its value is substituted
|
||||
# verbatim in the rules, until it is redefined.
|
||||
MODULE_DIR := src
|
||||
|
||||
# Regrettably, even 'simply expanded variables' will not put their currently
|
||||
# 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)/gl518sm.o \
|
||||
$(MODULE_DIR)/eeprom.o $(MODULE_DIR)/w83781d.o \
|
||||
$(MODULE_DIR)/lm80.o $(MODULE_DIR)/adm1021.o \
|
||||
$(MODULE_DIR)/adm9240.o $(MODULE_DIR)/ltc1710.o \
|
||||
$(MODULE_DIR)/i2c-dev.o
|
||||
|
||||
SRCHEADERFILES := $(MODULE_DIR)/sensors.h $(MODULE_DIR)/isa.h \
|
||||
$(MODULE_DIR)/smbus.h $(MODULE_DIR)/i2c-dev.h
|
||||
|
||||
# Include all dependency files
|
||||
INCLUDEFILES += $(SRCTARGETS:.o=.d)
|
||||
|
||||
all-src: $(SRCTARGETS)
|
||||
all :: all-src
|
||||
|
||||
install-src: all-src
|
||||
$(MKDIR) $(MODDIR) $(SYSINCLUDEDIR)
|
||||
$(INSTALL) -o root -g root -m 644 $(SRCTARGETS) $(MODDIR)
|
||||
$(INSTALL) -o root -g root -m 644 $(SRCHEADERFILES) $(SYSINCLUDEDIR)
|
||||
install :: install-src
|
||||
|
||||
clean-src:
|
||||
$(RM) $(SRCTARGETS) $(SRCTARGETS:.o=.d)
|
||||
clean :: clean-src
|
||||
-460
@@ -1,460 +0,0 @@
|
||||
/*
|
||||
adm1021.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
Copyright (c) 1998 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/malloc.h>
|
||||
#include "smbus.h"
|
||||
#include "sensors.h"
|
||||
#include "i2c.h"
|
||||
#include "version.h"
|
||||
|
||||
/* 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_DEVICE_ID 0x0FE /* should always read 0x41 */
|
||||
#define adm1021_REG_DIE_CODE 0x0FF
|
||||
/* These use different addresses for reading/writing */
|
||||
#define adm1021_REG_CONFIG_R 0x03
|
||||
#define adm1021_REG_CONFIG_W 0x09
|
||||
#define adm1021_REG_CONV_RATE_R 0x04
|
||||
#define adm1021_REG_CONV_RATE_W 0x0A
|
||||
#define adm1021_REG_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 note: 1021 uses normal integer signed-byte format*/
|
||||
#define TEMP_FROM_REG(val) (val > 127 ? val-256 : val)
|
||||
#define TEMP_TO_REG(val) (val < 0 ? val+256 : val)
|
||||
|
||||
/* Initial values */
|
||||
|
||||
/* Note: Eventhough 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 actuall triggers an alarm instead of
|
||||
clearing it. Weird, ey? --Phil */
|
||||
#define adm1021_INIT_TOS 60
|
||||
#define adm1021_INIT_THYST 20
|
||||
#define adm1021_INIT_REMOTE_TOS 60
|
||||
#define adm1021_INIT_REMOTE_THYST 20
|
||||
|
||||
/* Each client has this additional data */
|
||||
struct adm1021_data {
|
||||
int sysctl_id;
|
||||
|
||||
struct semaphore update_lock;
|
||||
char valid; /* !=0 if following fields are valid */
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
|
||||
u8 temp,temp_os,temp_hyst; /* Register values */
|
||||
u8 remote_temp,remote_temp_os,remote_temp_hyst,status,die_code;
|
||||
};
|
||||
|
||||
#ifdef MODULE
|
||||
extern int init_module(void);
|
||||
extern int cleanup_module(void);
|
||||
#endif /* MODULE */
|
||||
|
||||
static int adm1021_init(void);
|
||||
static int adm1021_cleanup(void);
|
||||
static int adm1021_attach_adapter(struct i2c_adapter *adapter);
|
||||
static int adm1021_detach_client(struct i2c_client *client);
|
||||
static int adm1021_command(struct i2c_client *client, unsigned int cmd,
|
||||
void *arg);
|
||||
static void adm1021_inc_use (struct i2c_client *client);
|
||||
static void adm1021_dec_use (struct i2c_client *client);
|
||||
static u16 swap_bytes(u16 val);
|
||||
static int adm1021_read_value(struct i2c_client *client, u8 reg);
|
||||
static int adm1021_write_value(struct i2c_client *client, u8 reg, u16 value);
|
||||
static void adm1021_temp(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void adm1021_remote_temp(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void adm1021_status(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void adm1021_die_code(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void adm1021_update_client(struct i2c_client *client);
|
||||
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver adm1021_driver = {
|
||||
/* name */ "adm1021 sensor chip driver",
|
||||
/* id */ I2C_DRIVERID_ADM1021,
|
||||
/* flags */ DF_NOTIFY,
|
||||
/* attach_adapter */ &adm1021_attach_adapter,
|
||||
/* detach_client */ &adm1021_detach_client,
|
||||
/* command */ &adm1021_command,
|
||||
/* inc_use */ &adm1021_inc_use,
|
||||
/* dec_use */ &adm1021_dec_use
|
||||
};
|
||||
|
||||
/* These files are created for each detected adm1021. This is just a template;
|
||||
though at first sight, you might think we could use a statically
|
||||
allocated list, we need some way to get back to the parent - which
|
||||
is done through one of the 'extra' fields which are initialized
|
||||
when a new copy is allocated. */
|
||||
static ctl_table adm1021_dir_table_template[] = {
|
||||
{ ADM1021_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &adm1021_temp },
|
||||
{ADM1021_SYSCTL_REMOTE_TEMP, "remote_temp", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &adm1021_remote_temp },
|
||||
{ADM1021_SYSCTL_DIE_CODE, "die_code", NULL, 0, 0444, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &adm1021_die_code },
|
||||
{ADM1021_SYSCTL_STATUS, "status", NULL, 0, 0444, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &adm1021_status },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
/* Used by init/cleanup */
|
||||
static int adm1021_initialized = 0;
|
||||
|
||||
/* I choose here for semi-static allocation. Complete dynamic
|
||||
allocation could also be used; the code needed for this would probably
|
||||
take more memory than the datastructure takes now. */
|
||||
#define MAX_adm1021_NR 9
|
||||
static struct i2c_client *adm1021_list[MAX_adm1021_NR];
|
||||
|
||||
|
||||
int adm1021_attach_adapter(struct i2c_adapter *adapter)
|
||||
{
|
||||
int address,err,i;
|
||||
struct i2c_client *new_client;
|
||||
struct adm1021_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 = 0x18; (! err) && (address <= 0x54); address ++) {
|
||||
/* Legal addresses: 0x18,19,20, 29,30,31, 52,53,54
|
||||
(Notice the two jumps) */
|
||||
if (address==0x21) { address=0x28; continue;}
|
||||
if (address==0x32) { address=0x51; continue;}
|
||||
/* Later on, we will keep a list of registered addresses for each
|
||||
adapter, and check whether they are used here */
|
||||
|
||||
/* Verify device is 1021 by checked special DEVICE_ID
|
||||
register (I wish all SMBus chips had this..) */
|
||||
if (smbus_read_byte_data(adapter,address,adm1021_REG_DEVICE_ID) != 0x41){
|
||||
continue;
|
||||
} else {
|
||||
printk("dm1021.o: Device found and passed detection.\n");
|
||||
}
|
||||
/* continue; */
|
||||
|
||||
/* Allocate space for a new client structure */
|
||||
if (! (new_client = kmalloc(sizeof(struct i2c_client) +
|
||||
sizeof(struct adm1021_data),
|
||||
GFP_KERNEL))) {
|
||||
err = -ENOMEM;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Find a place in our global list */
|
||||
for (i = 0; i < MAX_adm1021_NR; i++)
|
||||
if (! adm1021_list[i])
|
||||
break;
|
||||
if (i == MAX_adm1021_NR) {
|
||||
err = -ENOMEM;
|
||||
printk("adm1021.o: No empty slots left, recompile and heighten "
|
||||
"MAX_adm1021_NR!\n");
|
||||
goto ERROR1;
|
||||
}
|
||||
adm1021_list[i] = new_client;
|
||||
|
||||
/* Fill the new client structure with data */
|
||||
data = (struct adm1021_data *) (new_client + 1);
|
||||
new_client->data = data;
|
||||
new_client->id = i;
|
||||
new_client->addr = address;
|
||||
new_client->adapter = adapter;
|
||||
new_client->driver = &adm1021_driver;
|
||||
strcpy(new_client->name,"adm1021 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,"adm1021",
|
||||
adm1021_dir_table_template)) < 0)
|
||||
goto ERROR3;
|
||||
data->sysctl_id = err;
|
||||
err = 0;
|
||||
|
||||
/* Initialize the adm1021 chip */
|
||||
adm1021_write_value(new_client,adm1021_REG_TOS_W,
|
||||
TEMP_TO_REG(adm1021_INIT_TOS));
|
||||
adm1021_write_value(new_client,adm1021_REG_THYST_W,
|
||||
TEMP_TO_REG(adm1021_INIT_THYST));
|
||||
adm1021_write_value(new_client,adm1021_REG_REMOTE_TOS_W,
|
||||
TEMP_TO_REG(adm1021_INIT_REMOTE_TOS));
|
||||
adm1021_write_value(new_client,adm1021_REG_REMOTE_THYST_W,
|
||||
TEMP_TO_REG(adm1021_INIT_REMOTE_THYST));
|
||||
adm1021_write_value(new_client,adm1021_REG_CONFIG_W,0); /* Enable ADC and disable suspend mode */
|
||||
/* Set Conversion rate to 1/sec (this can be tinkered with) */
|
||||
adm1021_write_value(new_client,adm1021_REG_CONV_RATE_W,0x04);
|
||||
|
||||
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:
|
||||
adm1021_list[i] = NULL;
|
||||
ERROR1:
|
||||
kfree(new_client);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int adm1021_detach_client(struct i2c_client *client)
|
||||
{
|
||||
int err,i;
|
||||
for (i = 0; i < MAX_adm1021_NR; i++)
|
||||
if (client == adm1021_list[i])
|
||||
break;
|
||||
if ((i == MAX_adm1021_NR)) {
|
||||
printk("adm1021.o: Client to detach not found.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
sensors_deregister_entry(((struct adm1021_data *)(client->data))->sysctl_id);
|
||||
|
||||
if ((err = i2c_detach_client(client))) {
|
||||
printk("adm1021.o: Client deregistration failed, client not detached.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
adm1021_list[i] = NULL;
|
||||
kfree(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* No commands defined yet */
|
||||
int adm1021_command(struct i2c_client *client, unsigned int cmd, void *arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void adm1021_inc_use (struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_INC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
void adm1021_dec_use (struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_DEC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
u16 swap_bytes(u16 val)
|
||||
{
|
||||
return (val >> 8) | (val << 8);
|
||||
}
|
||||
|
||||
/* All registers are byte-sized */
|
||||
int adm1021_read_value(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
return smbus_read_byte_data(client->adapter,client->addr,reg);
|
||||
}
|
||||
|
||||
int adm1021_write_value(struct i2c_client *client, u8 reg, u16 value)
|
||||
{
|
||||
return smbus_write_byte_data(client->adapter,client->addr,reg,value);
|
||||
}
|
||||
|
||||
void adm1021_update_client(struct i2c_client *client)
|
||||
{
|
||||
struct adm1021_data *data = client->data;
|
||||
|
||||
down(&data->update_lock);
|
||||
|
||||
if ((jiffies - data->last_updated > HZ+HZ/2 ) ||
|
||||
(jiffies < data->last_updated) || ! data->valid) {
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("Starting adm1021 update\n");
|
||||
#endif
|
||||
|
||||
data->temp = adm1021_read_value(client,adm1021_REG_TEMP);
|
||||
data->temp_os = adm1021_read_value(client,adm1021_REG_TOS_R);
|
||||
data->temp_hyst = adm1021_read_value(client,adm1021_REG_THYST_R);
|
||||
data->remote_temp = adm1021_read_value(client,adm1021_REG_REMOTE_TEMP);
|
||||
data->remote_temp_os = adm1021_read_value(client,adm1021_REG_REMOTE_TOS_R);
|
||||
data->remote_temp_hyst = adm1021_read_value(client,adm1021_REG_REMOTE_THYST_R);
|
||||
data->die_code = adm1021_read_value(client,adm1021_REG_DIE_CODE);
|
||||
data->status = adm1021_read_value(client,adm1021_REG_STATUS);
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
up(&data->update_lock);
|
||||
}
|
||||
|
||||
|
||||
void adm1021_temp(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct adm1021_data *data = client->data;
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 0;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
adm1021_update_client(client);
|
||||
results[0] = TEMP_FROM_REG(data->temp_os);
|
||||
results[1] = TEMP_FROM_REG(data->temp_hyst);
|
||||
results[2] = TEMP_FROM_REG(data->temp);
|
||||
*nrels_mag = 3;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
if (*nrels_mag >= 1) {
|
||||
data->temp_os = TEMP_TO_REG(results[0]);
|
||||
adm1021_write_value(client,adm1021_REG_TOS_W,data->temp_os);
|
||||
}
|
||||
if (*nrels_mag >= 2) {
|
||||
data->temp_hyst = TEMP_TO_REG(results[1]);
|
||||
adm1021_write_value(client,adm1021_REG_THYST_W,data->temp_hyst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void adm1021_remote_temp(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct adm1021_data *data = client->data;
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 0;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
adm1021_update_client(client);
|
||||
results[0] = TEMP_FROM_REG(data->remote_temp_os);
|
||||
results[1] = TEMP_FROM_REG(data->remote_temp_hyst);
|
||||
results[2] = TEMP_FROM_REG(data->remote_temp);
|
||||
*nrels_mag = 3;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
if (*nrels_mag >= 1) {
|
||||
data->remote_temp_os = TEMP_TO_REG(results[0]);
|
||||
adm1021_write_value(client,adm1021_REG_REMOTE_TOS_W,data->remote_temp_os);
|
||||
}
|
||||
if (*nrels_mag >= 2) {
|
||||
data->remote_temp_hyst = TEMP_TO_REG(results[1]);
|
||||
adm1021_write_value(client,adm1021_REG_REMOTE_THYST_W,data->remote_temp_hyst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void adm1021_die_code(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct adm1021_data *data = client->data;
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 0;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
adm1021_update_client(client);
|
||||
results[0] = data->die_code;
|
||||
*nrels_mag = 1;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
/* Can't write to it */
|
||||
}
|
||||
}
|
||||
|
||||
void adm1021_status(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct adm1021_data *data = client->data;
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 0;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
adm1021_update_client(client);
|
||||
results[0] = data->status;
|
||||
*nrels_mag = 1;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
/* Can't write to it */
|
||||
}
|
||||
}
|
||||
|
||||
int adm1021_init(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
printk("adm1021.o version %s (%s)\n",LM_VERSION,LM_DATE);
|
||||
adm1021_initialized = 0;
|
||||
if ((res = i2c_add_driver(&adm1021_driver))) {
|
||||
printk("adm1021.o: Driver registration failed, module not inserted.\n");
|
||||
adm1021_cleanup();
|
||||
return res;
|
||||
}
|
||||
adm1021_initialized ++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adm1021_cleanup(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (adm1021_initialized >= 1) {
|
||||
if ((res = i2c_del_driver(&adm1021_driver))) {
|
||||
printk("adm1021.o: Driver deregistration failed, module not removed.\n");
|
||||
return res;
|
||||
}
|
||||
adm1021_initialized --;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MODULE
|
||||
|
||||
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
|
||||
MODULE_DESCRIPTION("adm1021 driver");
|
||||
|
||||
int init_module(void)
|
||||
{
|
||||
return adm1021_init();
|
||||
}
|
||||
|
||||
int cleanup_module(void)
|
||||
{
|
||||
return adm1021_cleanup();
|
||||
}
|
||||
|
||||
#endif /* MODULE */
|
||||
|
||||
-757
@@ -1,757 +0,0 @@
|
||||
/*
|
||||
adm9240.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
Copyright (c) 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
A couple notes about the ADM2940:
|
||||
|
||||
* It claims to be 'LM7x' register compatible. This must be in reference
|
||||
to only the LM78, because it is missing stuff to emulate LM75's as well.
|
||||
(like the Winbond W83781 does)
|
||||
|
||||
* This driver was written from rev. 0 of the PDF, but it seems well
|
||||
written and complete (unlike the W83781 which is horrible and has
|
||||
supposidly gone through a few revisions.. rev 0 of that one must
|
||||
have been in crayon on construction paper...)
|
||||
|
||||
* All analog inputs can range from 0 to 2.5, eventhough some inputs are
|
||||
marked as being 5V, 12V, etc. I don't have any real voltages going
|
||||
into my prototype, so I'm not sure that things are computed right,
|
||||
but at least the limits seem to be working OK.
|
||||
|
||||
* Another curiousity is that the fan_div seems to be read-only. I.e.,
|
||||
any written value to it doesn't seem to make any difference. The
|
||||
fan_div seems to be 'stuck' at 2 (which isn't a bad value in most cases).
|
||||
|
||||
|
||||
--Phil
|
||||
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/malloc.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/types.h>
|
||||
#include "smbus.h"
|
||||
#include "version.h"
|
||||
#include "isa.h"
|
||||
#include "sensors.h"
|
||||
#include "i2c.h"
|
||||
#include "compat.h"
|
||||
|
||||
/* Many ADM9240 constants specified below */
|
||||
|
||||
#define ADM9240_REG_IN_MAX(nr) (0x2b + (nr) * 2)
|
||||
#define ADM9240_REG_IN_MIN(nr) (0x2c + (nr) * 2)
|
||||
#define ADM9240_REG_IN(nr) (0x20 + (nr))
|
||||
|
||||
/* The ADM9240 registers */
|
||||
#define ADM9240_REG_TEST 0x15
|
||||
#define ADM9240_REG_ANALOG_OUT 0x19
|
||||
/* These are all read-only */
|
||||
#define ADM9240_REG_2_5V 0x20
|
||||
#define ADM9240_REG_VCCP1 0x21
|
||||
#define ADM9240_REG_3_3V 0x22
|
||||
#define ADM9240_REG_5V 0x23
|
||||
#define ADM9240_REG_12V 0x24
|
||||
#define ADM9240_REG_VCCP2 0x25
|
||||
#define ADM9240_REG_TEMP 0x27
|
||||
#define ADM9240_REG_FAN1 0x28
|
||||
#define ADM9240_REG_FAN2 0x29
|
||||
#define ADM9240_REG_COMPANY_ID 0x3E /* Should always read 0x23 */
|
||||
#define ADM9240_REG_DIE_REV 0x3F
|
||||
/* These are read/write */
|
||||
#define ADM9240_REG_2_5V_HIGH 0x2B
|
||||
#define ADM9240_REG_2_5V_LOW 0x2C
|
||||
#define ADM9240_REG_VCCP1_HIGH 0x2D
|
||||
#define ADM9240_REG_VCCP1_LOW 0x2E
|
||||
#define ADM9240_REG_3_3V_HIGH 0x2F
|
||||
#define ADM9240_REG_3_3V_LOW 0x30
|
||||
#define ADM9240_REG_5V_HIGH 0x31
|
||||
#define ADM9240_REG_5V_LOW 0x32
|
||||
#define ADM9240_REG_12V_HIGH 0x33
|
||||
#define ADM9240_REG_12V_LOW 0x34
|
||||
#define ADM9240_REG_VCCP2_HIGH 0x35
|
||||
#define ADM9240_REG_VCCP2_LOW 0x36
|
||||
#define ADM9240_REG_TOS 0x39
|
||||
#define ADM9240_REG_THYST 0x3A
|
||||
#define ADM9240_REG_FAN1_MIN 0x3B
|
||||
#define ADM9240_REG_FAN2_MIN 0x3C
|
||||
|
||||
#define ADM9240_REG_CONFIG 0x40
|
||||
#define ADM9240_REG_INT1_STAT 0x41
|
||||
#define ADM9240_REG_INT2_STAT 0x42
|
||||
#define ADM9240_REG_INT1_MASK 0x43
|
||||
#define ADM9240_REG_INT2_MASK 0x44
|
||||
|
||||
#define ADM9240_REG_COMPAT 0x45 /* dummy compat. register for other drivers? */
|
||||
#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_CONFIG 0x4B
|
||||
|
||||
/* Conversions. Rounding is only done on the TO_REG variants. */
|
||||
#define IN_TO_REG(val,nr) ((val) & 0xff)
|
||||
#define IN_FROM_REG(val,nr) (val)
|
||||
|
||||
#define FAN_TO_REG(val,div) ((val)==0?255:\
|
||||
((1350000+(val)*(div)/2)/((val)*(div))) & 0xff)
|
||||
#define FAN_FROM_REG(val,div) ((val)==0?-1:\
|
||||
(val)==255?0:1350000/((div)*(val)))
|
||||
|
||||
#define TEMP_FROM_REG(val) adm9240_temp_from_reg(val)
|
||||
|
||||
#define TEMP_LIMIT_TO_REG(val) (((val)<0?(((val)-50)/100)&0xff:\
|
||||
((val)+50)/100) & 0xff)
|
||||
#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*100)
|
||||
|
||||
#define ALARMS_FROM_REG(val) (val)
|
||||
|
||||
#define DIV_FROM_REG(val) (1 << val)
|
||||
#define DIV_TO_REG(val) (val==1?0:(val==2?1:(val==4?2:3)))
|
||||
|
||||
#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\
|
||||
(val)>=0x06?0:205-(val)*5)
|
||||
|
||||
/* Initial limits */
|
||||
#define ADM9240_INIT_IN_0 190
|
||||
#define ADM9240_INIT_IN_1 190
|
||||
#define ADM9240_INIT_IN_2 190
|
||||
#define ADM9240_INIT_IN_3 190
|
||||
#define ADM9240_INIT_IN_4 190
|
||||
#define ADM9240_INIT_IN_5 190
|
||||
|
||||
#define ADM9240_INIT_IN_PERCENTAGE 10
|
||||
|
||||
#define ADM9240_INIT_IN_MIN_0 \
|
||||
(ADM9240_INIT_IN_0 - ADM9240_INIT_IN_0 * ADM9240_INIT_IN_PERCENTAGE / 100)
|
||||
#define ADM9240_INIT_IN_MAX_0 \
|
||||
(ADM9240_INIT_IN_0 + ADM9240_INIT_IN_0 * ADM9240_INIT_IN_PERCENTAGE / 100)
|
||||
#define ADM9240_INIT_IN_MIN_1 \
|
||||
(ADM9240_INIT_IN_1 - ADM9240_INIT_IN_1 * ADM9240_INIT_IN_PERCENTAGE / 100)
|
||||
#define ADM9240_INIT_IN_MAX_1 \
|
||||
(ADM9240_INIT_IN_1 + ADM9240_INIT_IN_1 * ADM9240_INIT_IN_PERCENTAGE / 100)
|
||||
#define ADM9240_INIT_IN_MIN_2 \
|
||||
(ADM9240_INIT_IN_2 - ADM9240_INIT_IN_2 * ADM9240_INIT_IN_PERCENTAGE / 100)
|
||||
#define ADM9240_INIT_IN_MAX_2 \
|
||||
(ADM9240_INIT_IN_2 + ADM9240_INIT_IN_2 * ADM9240_INIT_IN_PERCENTAGE / 100)
|
||||
#define ADM9240_INIT_IN_MIN_3 \
|
||||
(ADM9240_INIT_IN_3 - ADM9240_INIT_IN_3 * ADM9240_INIT_IN_PERCENTAGE / 100)
|
||||
#define ADM9240_INIT_IN_MAX_3 \
|
||||
(ADM9240_INIT_IN_3 + ADM9240_INIT_IN_3 * ADM9240_INIT_IN_PERCENTAGE / 100)
|
||||
#define ADM9240_INIT_IN_MIN_4 \
|
||||
(ADM9240_INIT_IN_4 - ADM9240_INIT_IN_4 * ADM9240_INIT_IN_PERCENTAGE / 100)
|
||||
#define ADM9240_INIT_IN_MAX_4 \
|
||||
(ADM9240_INIT_IN_4 + ADM9240_INIT_IN_4 * ADM9240_INIT_IN_PERCENTAGE / 100)
|
||||
#define ADM9240_INIT_IN_MIN_5 \
|
||||
(ADM9240_INIT_IN_5 - ADM9240_INIT_IN_5 * ADM9240_INIT_IN_PERCENTAGE / 100)
|
||||
#define ADM9240_INIT_IN_MAX_5 \
|
||||
(ADM9240_INIT_IN_5 + ADM9240_INIT_IN_5 * ADM9240_INIT_IN_PERCENTAGE / 100)
|
||||
|
||||
#define ADM9240_INIT_FAN_MIN_1 3000
|
||||
#define ADM9240_INIT_FAN_MIN_2 3000
|
||||
|
||||
#define ADM9240_INIT_TEMP_OS_MAX 600
|
||||
#define ADM9240_INIT_TEMP_OS_HYST 500
|
||||
#define ADM9240_INIT_TEMP_HOT_MAX 700
|
||||
#define ADM9240_INIT_TEMP_HOT_HYST 600
|
||||
|
||||
#ifdef MODULE
|
||||
extern int init_module(void);
|
||||
extern int cleanup_module(void);
|
||||
#endif /* MODULE */
|
||||
|
||||
/* For each registered ADM9240, we need to keep some data in memory. That
|
||||
data is pointed to by adm9240_list[NR]->data. The structure itself is
|
||||
dynamically allocated, at the same time when a new adm9240 client is
|
||||
allocated. */
|
||||
struct adm9240_data {
|
||||
int sysctl_id;
|
||||
|
||||
struct semaphore update_lock;
|
||||
char valid; /* !=0 if 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 */
|
||||
u8 fan[2]; /* Register value */
|
||||
u8 fan_min[2]; /* Register value */
|
||||
u8 fan_div[2]; /* Register encoding, shifted right */
|
||||
int temp; /* Temp, shifted right */
|
||||
u8 temp_os_max; /* Register value */
|
||||
u8 temp_os_hyst; /* Register value */
|
||||
u16 alarms; /* Register encoding, combined */
|
||||
u8 analog_out; /* Register value */
|
||||
u8 vid; /* Register value combined */
|
||||
};
|
||||
|
||||
|
||||
static int adm9240_init(void);
|
||||
static int adm9240_cleanup(void);
|
||||
|
||||
static int adm9240_attach_adapter(struct i2c_adapter *adapter);
|
||||
static int adm9240_detach_client(struct i2c_client *client);
|
||||
static int adm9240_new_client(struct i2c_adapter *adapter,
|
||||
struct i2c_client *new_client);
|
||||
static void adm9240_remove_client(struct i2c_client *client);
|
||||
static int adm9240_command(struct i2c_client *client, unsigned int cmd,
|
||||
void *arg);
|
||||
static void adm9240_inc_use (struct i2c_client *client);
|
||||
static void adm9240_dec_use (struct i2c_client *client);
|
||||
|
||||
static long adm9240_temp_from_reg(u16 regs);
|
||||
|
||||
static int adm9240_read_value(struct i2c_client *client, u8 register);
|
||||
static int adm9240_write_value(struct i2c_client *client, u8 register, u8 value);
|
||||
static void adm9240_update_client(struct i2c_client *client);
|
||||
static void adm9240_init_client(struct i2c_client *client);
|
||||
|
||||
|
||||
static void adm9240_in(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void adm9240_fan(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void adm9240_temp(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void adm9240_alarms(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void adm9240_fan_div(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void adm9240_analog_out(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void adm9240_vid(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
|
||||
/* I choose here for semi-static ADM9240 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_ADM9240_NR 4
|
||||
static struct i2c_client *adm9240_list[MAX_ADM9240_NR];
|
||||
|
||||
/* 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 adm9240_driver = {
|
||||
/* name */ "ADM9240 sensor driver",
|
||||
/* id */ I2C_DRIVERID_ADM9240,
|
||||
/* flags */ DF_NOTIFY,
|
||||
/* attach_adapter */ &adm9240_attach_adapter,
|
||||
/* detach_client */ &adm9240_detach_client,
|
||||
/* command */ &adm9240_command,
|
||||
/* inc_use */ &adm9240_inc_use,
|
||||
/* dec_use */ &adm9240_dec_use
|
||||
};
|
||||
|
||||
/* Used by adm9240_init/cleanup */
|
||||
static int adm9240_initialized = 0;
|
||||
|
||||
/* The /proc/sys entries */
|
||||
/* These files are created for each detected ADM9240. 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 adm9240_dir_table_template[] = {
|
||||
{ ADM9240_SYSCTL_IN0, "2.5V", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &adm9240_in },
|
||||
{ ADM9240_SYSCTL_IN1, "Vccp1", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &adm9240_in },
|
||||
{ ADM9240_SYSCTL_IN2, "3.3V", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &adm9240_in },
|
||||
{ ADM9240_SYSCTL_IN3, "5V", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &adm9240_in },
|
||||
{ ADM9240_SYSCTL_IN4, "12V", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &adm9240_in },
|
||||
{ ADM9240_SYSCTL_IN5, "Vccp2", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &adm9240_in },
|
||||
{ ADM9240_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &adm9240_fan },
|
||||
{ ADM9240_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &adm9240_fan },
|
||||
{ ADM9240_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &adm9240_temp },
|
||||
{ ADM9240_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &adm9240_fan_div },
|
||||
{ ADM9240_SYSCTL_ALARMS, "alarms", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &adm9240_alarms },
|
||||
{ ADM9240_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &adm9240_analog_out },
|
||||
{ ADM9240_SYSCTL_VID, "vid", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &adm9240_vid },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
||||
int adm9240_attach_adapter(struct i2c_adapter *adapter)
|
||||
{
|
||||
int address,err,temp;
|
||||
struct i2c_client *new_client;
|
||||
const char *type_name,*client_name;
|
||||
|
||||
err = 0;
|
||||
/* The address of the ADM2940 must at least start somewhere in
|
||||
0x2C to 0x2F, but can be changed to be anyelse after that.
|
||||
(But, why??) */
|
||||
for (address = 0x2C; (! err) && (address <= 0x2f); address ++) {
|
||||
|
||||
/* Later on, we will keep a list of registered addresses for each
|
||||
adapter, and check whether they are used here */
|
||||
|
||||
if (smbus_read_byte_data(adapter,address,ADM9240_REG_COMPANY_ID) != 0x23)
|
||||
continue;
|
||||
|
||||
temp=smbus_read_byte_data(adapter,address,ADM9240_REG_DIE_REV);
|
||||
printk("adm9240.o: ADM9240 detected with die rev.: 0x%X\n",temp);
|
||||
type_name = "adm9240";
|
||||
client_name = "ADM9240 chip";
|
||||
|
||||
|
||||
/* Allocate space for a new client structure. To counter memory
|
||||
fragmentation somewhat, we only do one kmalloc. */
|
||||
if (! (new_client = kmalloc(sizeof(struct i2c_client) +
|
||||
sizeof(struct adm9240_data),
|
||||
GFP_KERNEL))) {
|
||||
err = -ENOMEM;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Fill the new client structure with data */
|
||||
new_client->data = (struct adm9240_data *) (new_client + 1);
|
||||
new_client->addr = address;
|
||||
strcpy(new_client->name,client_name);
|
||||
if ((err = adm9240_new_client(adapter,new_client)))
|
||||
goto ERROR2;
|
||||
|
||||
/* Tell i2c-core a new client has arrived */
|
||||
if ((err = i2c_attach_client(new_client)))
|
||||
goto ERROR3;
|
||||
|
||||
/* Register a new directory entry with module sensors */
|
||||
if ((err = sensors_register_entry(new_client,type_name,
|
||||
adm9240_dir_table_template)) < 0)
|
||||
goto ERROR4;
|
||||
((struct adm9240_data *) (new_client->data))->sysctl_id = err;
|
||||
err = 0;
|
||||
|
||||
/* Initialize the ADM9240 chip */
|
||||
adm9240_init_client(new_client);
|
||||
continue;
|
||||
|
||||
/* OK, this is not exactly good programming practice, usually. But it is
|
||||
very code-efficient in this case. */
|
||||
ERROR4:
|
||||
i2c_detach_client(new_client);
|
||||
ERROR3:
|
||||
adm9240_remove_client((struct i2c_client *) new_client);
|
||||
ERROR2:
|
||||
kfree(new_client);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int adm9240_detach_client(struct i2c_client *client)
|
||||
{
|
||||
int err,i;
|
||||
for (i = 0; i < MAX_ADM9240_NR; i++)
|
||||
if (client == adm9240_list[i])
|
||||
break;
|
||||
if ((i == MAX_ADM9240_NR)) {
|
||||
printk("adm9240.o: Client to detach not found.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
sensors_deregister_entry(((struct adm9240_data *)(client->data))->sysctl_id);
|
||||
|
||||
if ((err = i2c_detach_client(client))) {
|
||||
printk("adm9240.o: Client deregistration failed, client not detached.\n");
|
||||
return err;
|
||||
}
|
||||
adm9240_remove_client(client);
|
||||
kfree(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Find a free slot, and initialize most of the fields */
|
||||
int adm9240_new_client(struct i2c_adapter *adapter,
|
||||
struct i2c_client *new_client)
|
||||
{
|
||||
int i;
|
||||
struct adm9240_data *data;
|
||||
|
||||
/* First, seek out an empty slot */
|
||||
for(i = 0; i < MAX_ADM9240_NR; i++)
|
||||
if (! adm9240_list[i])
|
||||
break;
|
||||
if (i == MAX_ADM9240_NR) {
|
||||
printk("adm9240.o: No empty slots left, recompile and heighten "
|
||||
"MAX_ADM9240_NR!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
adm9240_list[i] = new_client;
|
||||
new_client->id = i;
|
||||
new_client->adapter = adapter;
|
||||
new_client->driver = &adm9240_driver;
|
||||
data = new_client->data;
|
||||
data->valid = 0;
|
||||
data->update_lock = MUTEX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Inverse of adm9240_new_client */
|
||||
void adm9240_remove_client(struct i2c_client *client)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAX_ADM9240_NR; i++)
|
||||
if (client == adm9240_list[i])
|
||||
adm9240_list[i] = NULL;
|
||||
}
|
||||
|
||||
/* No commands defined yet */
|
||||
int adm9240_command(struct i2c_client *client, unsigned int cmd, void *arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void adm9240_inc_use (struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_INC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
void adm9240_dec_use (struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_DEC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
long adm9240_temp_from_reg(u16 temp)
|
||||
{
|
||||
if (temp < 256)
|
||||
return (((temp & 0x1fe) >> 1) * 10) + ((temp & 1) * 5);
|
||||
else
|
||||
return ((((temp & 0x01fe) >> 1) - 255) * 10) - ((temp & 1) * 5);
|
||||
}
|
||||
|
||||
int adm9240_read_value(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
return 0xFF & smbus_read_byte_data(client->adapter,client->addr, reg);
|
||||
}
|
||||
|
||||
int adm9240_write_value(struct i2c_client *client, u8 reg, u8 value)
|
||||
{
|
||||
return smbus_write_byte_data(client->adapter, client->addr, reg,value);
|
||||
}
|
||||
|
||||
/* Called when we have found a new ADM9240. It should set limits, etc. */
|
||||
void adm9240_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 */
|
||||
adm9240_write_value(client,ADM9240_REG_CONFIG,0x80);
|
||||
|
||||
adm9240_write_value(client,ADM9240_REG_IN_MIN(0),IN_TO_REG(ADM9240_INIT_IN_MIN_0,0));
|
||||
adm9240_write_value(client,ADM9240_REG_IN_MAX(0),IN_TO_REG(ADM9240_INIT_IN_MAX_0,0));
|
||||
adm9240_write_value(client,ADM9240_REG_IN_MIN(1),IN_TO_REG(ADM9240_INIT_IN_MIN_1,1));
|
||||
adm9240_write_value(client,ADM9240_REG_IN_MAX(1),IN_TO_REG(ADM9240_INIT_IN_MAX_1,1));
|
||||
adm9240_write_value(client,ADM9240_REG_IN_MIN(2),IN_TO_REG(ADM9240_INIT_IN_MIN_2,2));
|
||||
adm9240_write_value(client,ADM9240_REG_IN_MAX(2),IN_TO_REG(ADM9240_INIT_IN_MAX_2,2));
|
||||
adm9240_write_value(client,ADM9240_REG_IN_MIN(3),IN_TO_REG(ADM9240_INIT_IN_MIN_3,3));
|
||||
adm9240_write_value(client,ADM9240_REG_IN_MAX(3),IN_TO_REG(ADM9240_INIT_IN_MAX_3,3));
|
||||
adm9240_write_value(client,ADM9240_REG_IN_MIN(4),IN_TO_REG(ADM9240_INIT_IN_MIN_4,4));
|
||||
adm9240_write_value(client,ADM9240_REG_IN_MAX(4),IN_TO_REG(ADM9240_INIT_IN_MAX_4,4));
|
||||
adm9240_write_value(client,ADM9240_REG_IN_MIN(5),IN_TO_REG(ADM9240_INIT_IN_MIN_5,5));
|
||||
adm9240_write_value(client,ADM9240_REG_IN_MAX(5),IN_TO_REG(ADM9240_INIT_IN_MAX_5,5));
|
||||
adm9240_write_value(client,ADM9240_REG_FAN1_MIN,FAN_TO_REG(ADM9240_INIT_FAN_MIN_1,2));
|
||||
adm9240_write_value(client,ADM9240_REG_FAN2_MIN,FAN_TO_REG(ADM9240_INIT_FAN_MIN_2,2));
|
||||
adm9240_write_value(client,ADM9240_REG_TOS,
|
||||
TEMP_LIMIT_TO_REG(ADM9240_INIT_TEMP_OS_MAX));
|
||||
adm9240_write_value(client,ADM9240_REG_THYST,
|
||||
TEMP_LIMIT_TO_REG(ADM9240_INIT_TEMP_OS_HYST));
|
||||
adm9240_write_value(client,ADM9240_REG_TEMP_CONFIG,0x00);
|
||||
|
||||
/* Start monitoring */
|
||||
adm9240_write_value(client,ADM9240_REG_CONFIG,0x0B);
|
||||
}
|
||||
|
||||
void adm9240_update_client(struct i2c_client *client)
|
||||
{
|
||||
struct adm9240_data *data = client->data;
|
||||
u8 i;
|
||||
|
||||
down(&data->update_lock);
|
||||
|
||||
if ((jiffies - data->last_updated > 2*HZ ) ||
|
||||
(jiffies < data->last_updated) || ! data->valid) {
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("Starting adm9240 update\n");
|
||||
#endif
|
||||
for (i = 0; i <= 5; i++) {
|
||||
data->in[i] = adm9240_read_value(client,ADM9240_REG_IN(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));
|
||||
}
|
||||
data->fan[0] = adm9240_read_value(client,ADM9240_REG_FAN1);
|
||||
data->fan_min[0] = adm9240_read_value(client,ADM9240_REG_FAN1_MIN);
|
||||
data->fan[1] = adm9240_read_value(client,ADM9240_REG_FAN2);
|
||||
data->fan_min[1] = adm9240_read_value(client,ADM9240_REG_FAN2_MIN);
|
||||
data->temp = (adm9240_read_value(client,ADM9240_REG_TEMP) << 1) +
|
||||
((adm9240_read_value(client,ADM9240_REG_TEMP_CONFIG) & 0x80) >> 7);
|
||||
data->temp_os_max = adm9240_read_value(client,ADM9240_REG_TOS);
|
||||
data->temp_os_hyst = adm9240_read_value(client,ADM9240_REG_THYST);
|
||||
|
||||
i = adm9240_read_value(client,ADM9240_REG_VID_FAN_DIV);
|
||||
data->fan_div[0] = (i >> 4) & 0x03;
|
||||
data->fan_div[1] = (i >> 6) & 0x03;
|
||||
data->vid = i & 0x0f;
|
||||
data->vid |= (adm9240_read_value(client,ADM9240_REG_VID4) & 0x01) << 4;
|
||||
|
||||
data->alarms = adm9240_read_value(client,ADM9240_REG_INT1_STAT) +
|
||||
(adm9240_read_value(client,ADM9240_REG_INT2_STAT) << 8);
|
||||
data->analog_out = adm9240_read_value(client,ADM9240_REG_ANALOG_OUT);
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
up(&data->update_lock);
|
||||
}
|
||||
|
||||
|
||||
/* The next few functions are the call-back functions of the /proc/sys and
|
||||
sysctl files. Which function is used is defined in the ctl_table in
|
||||
the extra1 field.
|
||||
Each function must return the magnitude (power of 10 to divide the date
|
||||
with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
|
||||
put a maximum of *nrels elements in results reflecting the data of this
|
||||
file, and set *nrels to the number it actually put in it, if operation==
|
||||
SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
|
||||
results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
|
||||
Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
|
||||
large enough (by checking the incoming value of *nrels). This is not very
|
||||
good practice, but as long as you put less than about 5 values in results,
|
||||
you can assume it is large enough. */
|
||||
void adm9240_in(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct adm9240_data *data = client->data;
|
||||
int nr = ctl_name - ADM9240_SYSCTL_IN0;
|
||||
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 2;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
adm9240_update_client(client);
|
||||
results[0] = IN_FROM_REG(data->in_min[nr],nr);
|
||||
results[1] = IN_FROM_REG(data->in_max[nr],nr);
|
||||
results[2] = IN_FROM_REG(data->in[nr],nr);
|
||||
*nrels_mag = 3;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
if (*nrels_mag >= 1) {
|
||||
data->in_min[nr] = IN_TO_REG(results[0],nr);
|
||||
adm9240_write_value(client,ADM9240_REG_IN_MIN(nr),data->in_min[nr]);
|
||||
}
|
||||
if (*nrels_mag >= 2) {
|
||||
data->in_max[nr] = IN_TO_REG(results[1],nr);
|
||||
adm9240_write_value(client,ADM9240_REG_IN_MAX(nr),data->in_max[nr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void adm9240_fan(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct adm9240_data *data = client->data;
|
||||
int nr = ctl_name - ADM9240_SYSCTL_FAN1 + 1;
|
||||
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 0;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
adm9240_update_client(client);
|
||||
results[0] = FAN_FROM_REG(data->fan_min[nr-1],
|
||||
DIV_FROM_REG(data->fan_div[nr-1]));
|
||||
results[1] = FAN_FROM_REG(data->fan[nr-1],
|
||||
DIV_FROM_REG(data->fan_div[nr-1]));
|
||||
*nrels_mag = 2;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
if (*nrels_mag >= 1) {
|
||||
data->fan_min[nr-1] = FAN_TO_REG(results[0],
|
||||
DIV_FROM_REG(data->fan_div[nr-1]));
|
||||
adm9240_write_value(client,nr==1?ADM9240_REG_FAN1_MIN:ADM9240_REG_FAN2_MIN,
|
||||
data->fan_min[nr-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void adm9240_temp(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct adm9240_data *data = client->data;
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 1;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
adm9240_update_client(client);
|
||||
results[0] = TEMP_LIMIT_FROM_REG(data->temp_os_max);
|
||||
results[1] = TEMP_LIMIT_FROM_REG(data->temp_os_hyst);
|
||||
results[2] = TEMP_FROM_REG(data->temp);
|
||||
*nrels_mag = 3;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
if (*nrels_mag >= 1) {
|
||||
data->temp_os_max = TEMP_LIMIT_TO_REG(results[0]);
|
||||
adm9240_write_value(client,ADM9240_REG_TOS,data->temp_os_max);
|
||||
}
|
||||
if (*nrels_mag >= 2) {
|
||||
data->temp_os_hyst = TEMP_LIMIT_TO_REG(results[1]);
|
||||
adm9240_write_value(client,ADM9240_REG_THYST,data->temp_os_hyst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void adm9240_alarms(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct adm9240_data *data = client->data;
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 0;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
adm9240_update_client(client);
|
||||
results[0] = ALARMS_FROM_REG(data->alarms);
|
||||
*nrels_mag = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void adm9240_fan_div(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct adm9240_data *data = client->data;
|
||||
int old;
|
||||
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 0;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
adm9240_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 = adm9240_read_value(client,ADM9240_REG_VID_FAN_DIV);
|
||||
if (*nrels_mag >= 2) {
|
||||
data->fan_div[1] = DIV_TO_REG(results[1]);
|
||||
old = (old & 0xcf) | (data->fan_div[1] << 6);
|
||||
}
|
||||
if (*nrels_mag >= 1) {
|
||||
data->fan_div[0] = DIV_TO_REG(results[0]);
|
||||
old = (old & 0x3f) | (data->fan_div[0] << 4);
|
||||
adm9240_write_value(client,ADM9240_REG_VID_FAN_DIV,old);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void adm9240_analog_out(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct adm9240_data *data = client->data;
|
||||
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 0;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
adm9240_update_client(client);
|
||||
results[0] = data->analog_out;
|
||||
*nrels_mag = 1;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
if (*nrels_mag >= 1) {
|
||||
data->analog_out = results[0];
|
||||
adm9240_write_value(client,ADM9240_REG_ANALOG_OUT,data->analog_out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void adm9240_vid(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct adm9240_data *data = client->data;
|
||||
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 2;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
adm9240_update_client(client);
|
||||
results[0] = VID_FROM_REG(data->vid);
|
||||
*nrels_mag = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int adm9240_init(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
printk("adm9240.o version %s (%s)\n",LM_VERSION,LM_DATE);
|
||||
adm9240_initialized = 0;
|
||||
|
||||
if ((res =i2c_add_driver(&adm9240_driver))) {
|
||||
printk("adm9240.o: Driver registration failed, module not inserted.\n");
|
||||
adm9240_cleanup();
|
||||
return res;
|
||||
}
|
||||
adm9240_initialized ++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adm9240_cleanup(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (adm9240_initialized >= 1) {
|
||||
if ((res = i2c_del_driver(&adm9240_driver))) {
|
||||
printk("adm9240.o: Driver deregistration failed, module not removed.\n");
|
||||
return res;
|
||||
}
|
||||
adm9240_initialized --;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MODULE
|
||||
|
||||
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
|
||||
MODULE_DESCRIPTION("ADM9240 driver");
|
||||
|
||||
int init_module(void)
|
||||
{
|
||||
return adm9240_init();
|
||||
}
|
||||
|
||||
int cleanup_module(void)
|
||||
{
|
||||
return adm9240_cleanup();
|
||||
}
|
||||
|
||||
#endif /* MODULE */
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
compat.h - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef SENSORS_COMPAT_H
|
||||
#define SENSORS_COMPAT_H
|
||||
|
||||
/* This useful macro is not defined in the 2.0 kernels */
|
||||
|
||||
#include <linux/version.h>
|
||||
#ifndef KERNEL_VERSION
|
||||
#define KERNEL_VERSION(a,b,c) (((a) << 16) | ((b) << 8) | (c))
|
||||
#endif
|
||||
|
||||
#ifdef MODULE
|
||||
#include <linux/module.h>
|
||||
#ifndef MODULE_AUTHOR
|
||||
#define MODULE_AUTHOR(whatever)
|
||||
#endif
|
||||
#ifndef MODULE_DESCRIPTION
|
||||
#define MODULE_DESCRIPTION(whatever)
|
||||
#endif
|
||||
#endif /* def MODULE */
|
||||
|
||||
/* copy_from/to_usr is called memcpy_from/to_fs in 2.0 kernels
|
||||
get_user was redefined in 2.1 kernels to use two arguments, and returns
|
||||
an error code */
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,4))
|
||||
#define copy_from_user memcpy_fromfs
|
||||
#define copy_to_user memcpy_tofs
|
||||
#define get_user_data(to,from) ((to) = get_user(from),0)
|
||||
#else
|
||||
#include <asm/uaccess.h>
|
||||
#define get_user_data(to,from) get_user(to,from)
|
||||
#endif
|
||||
|
||||
/* Add a scheduling fix for the new code in kernel 2.1.127 */
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,127))
|
||||
#define schedule_timeout(x) ( current->timeout = jiffies + (x), schedule() )
|
||||
#endif
|
||||
|
||||
/* If the new PCI interface is not present, fall back on the old PCI BIOS
|
||||
interface. We also define some things to unite both interfaces. Not
|
||||
very nice, but it works like a charm.
|
||||
device is the 2.1 struct pci_dev, bus is the 2.0 bus number, dev is the
|
||||
2.0 device/function code, com is the PCI command, and res is the result. */
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,54))
|
||||
#define pci_present pcibios_present
|
||||
#define pci_read_config_byte_united(device,bus,dev,com,res) \
|
||||
pcibios_read_config_byte(bus,dev,com,res);
|
||||
#define pci_read_config_word_united(device,bus,dev,com,res) \
|
||||
pcibios_read_config_word(bus,dev,com,res);
|
||||
#define pci_write_config_byte_united(device,bus,dev,com,res) \
|
||||
pcibios_write_config_byte(bus,dev,com,res);
|
||||
#define pci_write_config_word_united(device,bus,dev,com,res) \
|
||||
pcibios_write_config_word(bus,dev,com,res);
|
||||
#else
|
||||
#define pci_read_config_byte_united(device,bus,dev,com,res) \
|
||||
pci_read_config_byte(device,com,res);
|
||||
#define pci_read_config_word_united(device,bus,dev,com,res) \
|
||||
pci_read_config_word(device,com,res);
|
||||
#define pci_write_config_byte_united(device,bus,dev,com,res) \
|
||||
pci_write_config_byte(device,com,res);
|
||||
#define pci_write_config_word_united(device,bus,dev,com,res) \
|
||||
pci_write_config_byte(device,com,res);
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* SENSORS_COMPAT_H */
|
||||
-387
@@ -1,387 +0,0 @@
|
||||
/*
|
||||
eeprom.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
Copyright (c) 1998 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/malloc.h>
|
||||
#include "smbus.h"
|
||||
#include "sensors.h"
|
||||
#include "i2c.h"
|
||||
#include "version.h"
|
||||
|
||||
/* Many constants specified below */
|
||||
|
||||
/* EEPROM memory types: */
|
||||
#define ONE_K 1
|
||||
#define TWO_K 2
|
||||
#define FOUR_K 3
|
||||
#define EIGHT_K 4
|
||||
#define SIXTEEN_K 5
|
||||
|
||||
/* Conversions */
|
||||
/* Size of EEPROM in bytes */
|
||||
#define EEPROM_SIZE 128
|
||||
|
||||
/* Each client has this additional data */
|
||||
struct eeprom_data {
|
||||
int sysctl_id;
|
||||
|
||||
struct semaphore update_lock;
|
||||
char valid; /* !=0 if following fields are valid */
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
|
||||
u8 data[EEPROM_SIZE]; /* Register values */
|
||||
int memtype;
|
||||
};
|
||||
|
||||
#ifdef MODULE
|
||||
extern int init_module(void);
|
||||
extern int cleanup_module(void);
|
||||
#endif /* MODULE */
|
||||
|
||||
static int eeprom_init(void);
|
||||
static int eeprom_cleanup(void);
|
||||
|
||||
static int eeprom_attach_adapter(struct i2c_adapter *adapter);
|
||||
static int eeprom_detach_client(struct i2c_client *client);
|
||||
static int eeprom_command(struct i2c_client *client, unsigned int cmd,
|
||||
void *arg);
|
||||
|
||||
static void eeprom_inc_use (struct i2c_client *client);
|
||||
static void eeprom_dec_use (struct i2c_client *client);
|
||||
|
||||
static u16 swap_bytes(u16 val);
|
||||
|
||||
static int eeprom_read_value(struct i2c_client *client, u8 reg);
|
||||
static int eeprom_write_value(struct i2c_client *client, u8 reg, u16 value);
|
||||
|
||||
static void eeprom_contents(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void eeprom_update_client(struct i2c_client *client);
|
||||
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver eeprom_driver = {
|
||||
/* name */ "EEPROM READER",
|
||||
/* id */ I2C_DRIVERID_EEPROM,
|
||||
/* flags */ DF_NOTIFY,
|
||||
/* attach_adapter */ &eeprom_attach_adapter,
|
||||
/* detach_client */ &eeprom_detach_client,
|
||||
/* command */ &eeprom_command,
|
||||
/* inc_use */ &eeprom_inc_use,
|
||||
/* dec_use */ &eeprom_dec_use
|
||||
};
|
||||
|
||||
/* These files are created for each detected EEPROM. 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 eeprom_dir_table_template[] = {
|
||||
{ EEPROM_SYSCTL1, "data0-15", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &eeprom_contents },
|
||||
{ EEPROM_SYSCTL2, "data16-31", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &eeprom_contents },
|
||||
{ EEPROM_SYSCTL3, "data32-47", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &eeprom_contents },
|
||||
{ EEPROM_SYSCTL4, "data48-63", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &eeprom_contents },
|
||||
{ EEPROM_SYSCTL5, "data64-79", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &eeprom_contents },
|
||||
{ EEPROM_SYSCTL6, "data80-95", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &eeprom_contents },
|
||||
{ EEPROM_SYSCTL7, "data96-111", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &eeprom_contents },
|
||||
{ EEPROM_SYSCTL8, "data112-127", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &eeprom_contents },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
/* Used by init/cleanup */
|
||||
static int eeprom_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_EEPROM_NR 8
|
||||
static struct i2c_client *eeprom_list[MAX_EEPROM_NR];
|
||||
|
||||
|
||||
int eeprom_attach_adapter(struct i2c_adapter *adapter)
|
||||
{
|
||||
int address,err,i;
|
||||
struct i2c_client *new_client;
|
||||
struct eeprom_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). */
|
||||
|
||||
/* Serial EEPROMs for SMBus use addresses from 0x50 to 0x57 */
|
||||
for (address = 0x50; (! err) && (address <= 0x57); address ++) {
|
||||
|
||||
/* Later on, we will keep a list of registered addresses for each
|
||||
adapter, and check whether they are used here */
|
||||
|
||||
if (smbus_read_byte(adapter,address) < 0) {
|
||||
#ifdef DEBUG
|
||||
printk("eeprom.o: No eeprom found at: 0x%X\n",address);
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Real detection code goes here */
|
||||
|
||||
/* Allocate space for a new client structure */
|
||||
if (! (new_client = kmalloc(sizeof(struct i2c_client) +
|
||||
sizeof(struct eeprom_data),
|
||||
GFP_KERNEL))) {
|
||||
err = -ENOMEM;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Find a place in our global list */
|
||||
for (i = 0; i < MAX_EEPROM_NR; i++)
|
||||
if (! eeprom_list[i])
|
||||
break;
|
||||
if (i == MAX_EEPROM_NR) {
|
||||
err = -ENOMEM;
|
||||
printk("eeprom.o: No empty slots left, recompile and heighten "
|
||||
"MAX_EEPROM_NR!\n");
|
||||
goto ERROR1;
|
||||
}
|
||||
eeprom_list[i] = new_client;
|
||||
|
||||
/* Fill the new client structure with data */
|
||||
data = (struct eeprom_data *) (new_client + 1);
|
||||
new_client->data = data;
|
||||
new_client->id = i;
|
||||
new_client->addr = address;
|
||||
new_client->adapter = adapter;
|
||||
new_client->driver = &eeprom_driver;
|
||||
strcpy(new_client->name,"EEPROM 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,"eeprom",
|
||||
eeprom_dir_table_template)) < 0)
|
||||
goto ERROR3;
|
||||
data->sysctl_id = err;
|
||||
err = 0;
|
||||
|
||||
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:
|
||||
eeprom_list[i] = NULL;
|
||||
ERROR1:
|
||||
kfree(new_client);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int eeprom_detach_client(struct i2c_client *client)
|
||||
{
|
||||
int err,i;
|
||||
for (i = 0; i < MAX_EEPROM_NR; i++)
|
||||
if (client == eeprom_list[i])
|
||||
break;
|
||||
if ((i == MAX_EEPROM_NR)) {
|
||||
printk("eeprom.o: Client to detach not found.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
sensors_deregister_entry(((struct eeprom_data *)(client->data))->sysctl_id);
|
||||
|
||||
if ((err = i2c_detach_client(client))) {
|
||||
printk("eeprom.o: Client deregistration failed, client not detached.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
eeprom_list[i] = NULL;
|
||||
kfree(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* No commands defined yet */
|
||||
int eeprom_command(struct i2c_client *client, unsigned int cmd, void *arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void eeprom_inc_use (struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_INC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
void eeprom_dec_use (struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_DEC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
u16 swap_bytes(u16 val)
|
||||
{
|
||||
return (val >> 8) | (val << 8);
|
||||
}
|
||||
|
||||
/* No swapping needed here! */
|
||||
int eeprom_write_value(struct i2c_client *client, u8 reg, u16 value)
|
||||
{/*
|
||||
if (reg == EEPROM_REG_CONF)
|
||||
return smbus_write_byte_data(client->adapter,client->addr,reg,value);
|
||||
else
|
||||
return smbus_write_word_data(client->adapter,client->addr,reg,value); */
|
||||
|
||||
/* No writes yet (PAE) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void eeprom_update_client(struct i2c_client *client)
|
||||
{
|
||||
struct eeprom_data *data = client->data;
|
||||
int i;
|
||||
|
||||
down(&data->update_lock);
|
||||
|
||||
if ((jiffies - data->last_updated > HZ+HZ/2 ) ||
|
||||
(jiffies < data->last_updated) || ! data->valid) {
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("Starting eeprom update\n");
|
||||
#endif
|
||||
|
||||
if (smbus_write_byte(client->adapter,client->addr,0)) {
|
||||
#ifdef DEBUG
|
||||
printk("eeprom read start has failed!\n");
|
||||
#endif
|
||||
}
|
||||
for (i=0;i<EEPROM_SIZE;i++) {
|
||||
data->data[i] = (u8)smbus_read_byte(client->adapter,client->addr);
|
||||
}
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
up(&data->update_lock);
|
||||
}
|
||||
|
||||
|
||||
void eeprom_contents(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
int i;
|
||||
int base=0;
|
||||
struct eeprom_data *data = client->data;
|
||||
|
||||
if (ctl_name == EEPROM_SYSCTL2){ base=16; }
|
||||
if (ctl_name == EEPROM_SYSCTL3){ base=32; }
|
||||
if (ctl_name == EEPROM_SYSCTL4){ base=48; }
|
||||
if (ctl_name == EEPROM_SYSCTL5){ base=64; }
|
||||
if (ctl_name == EEPROM_SYSCTL6){ base=80; }
|
||||
if (ctl_name == EEPROM_SYSCTL7){ base=96; }
|
||||
if (ctl_name == EEPROM_SYSCTL8){ base=112; }
|
||||
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 0;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
eeprom_update_client(client);
|
||||
for (i=0; i<16; i++) {
|
||||
results[i]=data->data[i + base];
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printk("eeprom.o: 0x%X EEPROM Contents (base %d): ",client->addr,base);
|
||||
for (i=0; i<16; i++) {
|
||||
printk(" 0x%X",data->data[i + base]);
|
||||
}
|
||||
printk(" .\n");
|
||||
#endif
|
||||
*nrels_mag = 16;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
|
||||
/* No writes to the EEPROM (yet, anyway) (PAE) */
|
||||
printk("eeprom.o: No writes to EEPROMs supported!\n");
|
||||
}
|
||||
}
|
||||
|
||||
int eeprom_init(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
printk("eeprom.o version %s (%s)\n",LM_VERSION,LM_DATE);
|
||||
eeprom_initialized = 0;
|
||||
if ((res = i2c_add_driver(&eeprom_driver))) {
|
||||
printk("eeprom.o: Driver registration failed, module not inserted.\n");
|
||||
eeprom_cleanup();
|
||||
return res;
|
||||
}
|
||||
eeprom_initialized ++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eeprom_cleanup(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (eeprom_initialized >= 1) {
|
||||
if ((res = i2c_del_driver(&eeprom_driver))) {
|
||||
printk("eeprom.o: Driver deregistration failed, module not removed.\n");
|
||||
return res;
|
||||
}
|
||||
} else
|
||||
eeprom_initialized --;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MODULE
|
||||
|
||||
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
|
||||
MODULE_DESCRIPTION("EEPROM driver");
|
||||
|
||||
int init_module(void)
|
||||
{
|
||||
return eeprom_init();
|
||||
}
|
||||
|
||||
int cleanup_module(void)
|
||||
{
|
||||
return eeprom_cleanup();
|
||||
}
|
||||
|
||||
#endif /* MODULE */
|
||||
|
||||
-680
@@ -1,680 +0,0 @@
|
||||
/*
|
||||
gl518sm.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
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_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 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?0:(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 VDD_TO_REG(val) ((((val)*10+11)/23) & 0xff)
|
||||
#define VDD_FROM_REG(val) (((val)*23)/10)
|
||||
|
||||
#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
|
||||
#define DIV_FROM_REG(val) (1 << (val))
|
||||
|
||||
#define ALARMS_FROM_REG(val) val
|
||||
|
||||
#define BEEP_ENABLE_TO_REG(val) ((val)?0:1)
|
||||
#define BEEP_ENABLE_FROM_REG(val) ((val)?0:1)
|
||||
|
||||
#define BEEPS_TO_REG(val) ((val) & 0x7f)
|
||||
#define BEEPS_FROM_REG(val) (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 300
|
||||
#define GL518_INIT_VIN_2 300
|
||||
#define GL518_INIT_VIN_3 300
|
||||
#define GL518_INIT_VDD 500
|
||||
|
||||
#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;
|
||||
|
||||
u8 revision;
|
||||
|
||||
struct semaphore update_lock;
|
||||
char valid; /* !=0 if following fields are valid */
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
|
||||
u8 voltage[4]; /* Register values; [0] = VDD */
|
||||
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,beeps; /* Register value */
|
||||
u8 fan_div[2]; /* Register encoding, shifted right */
|
||||
u8 beep_enable; /* Boolean */
|
||||
};
|
||||
|
||||
#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 u16 swap_bytes(u16 val);
|
||||
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);
|
||||
static void gl518_beep(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 },
|
||||
{ GL518_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &gl518_beep },
|
||||
{ 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;
|
||||
u8 revision;
|
||||
char name[11];
|
||||
|
||||
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;
|
||||
|
||||
revision = smbus_read_byte_data(adapter,address,GL518_REG_REVISION);
|
||||
if ((revision != 0x00) && (revision != 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;
|
||||
sprintf(new_client->name,"GL518SM chip rev. %02x",revision);
|
||||
data->valid = 0;
|
||||
data->update_lock = MUTEX;
|
||||
data->revision = revision;
|
||||
|
||||
/* Tell i2c-core a new client has arrived */
|
||||
if ((err = i2c_attach_client(new_client)))
|
||||
goto ERROR2;
|
||||
|
||||
sprintf(name,"gl518sm-r%02x",revision);
|
||||
/* Register a new directory entry with module sensors */
|
||||
if ((err = sensors_register_entry(new_client,name,
|
||||
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);
|
||||
/* Never interrupts */
|
||||
gl518_write_value(new_client,GL518_REG_MASK,0x00);
|
||||
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_MAX_1) << 8) |
|
||||
IN_TO_REG(GL518_INIT_VIN_MIN_1));
|
||||
gl518_write_value(new_client,GL518_REG_VIN2_LIMIT,
|
||||
(IN_TO_REG(GL518_INIT_VIN_MAX_2) << 8) |
|
||||
IN_TO_REG(GL518_INIT_VIN_MIN_2));
|
||||
gl518_write_value(new_client,GL518_REG_VIN3_LIMIT,
|
||||
(IN_TO_REG(GL518_INIT_VIN_MAX_3) << 8) |
|
||||
IN_TO_REG(GL518_INIT_VIN_MIN_3));
|
||||
gl518_write_value(new_client,GL518_REG_VDD_LIMIT,
|
||||
(VDD_TO_REG(GL518_INIT_VDD_MAX) << 8) |
|
||||
VDD_TO_REG(GL518_INIT_VDD_MIN));
|
||||
/* Clear status register (bit 5=1), start (bit6=1) */
|
||||
gl518_write_value(new_client,GL518_REG_CONF,0x24);
|
||||
gl518_write_value(new_client,GL518_REG_CONF,0x44);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
u16 swap_bytes(u16 val)
|
||||
{
|
||||
return (val >> 8) | (val << 8);
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
int gl518_read_value(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
if ((reg >= 0x07) && (reg <= 0x0c))
|
||||
return swap_bytes(smbus_read_word_data(client->adapter,client->addr,reg));
|
||||
else
|
||||
return smbus_read_byte_data(client->adapter,client->addr,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. */
|
||||
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,
|
||||
swap_bytes(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
|
||||
|
||||
if (data->revision != 0x00) {
|
||||
data->voltage[0] = gl518_read_value(client,GL518_REG_VDD);
|
||||
data->voltage[1] = gl518_read_value(client,GL518_REG_VIN1);
|
||||
data->voltage[2] = gl518_read_value(client,GL518_REG_VIN2);
|
||||
}
|
||||
data->voltage[3] = gl518_read_value(client,GL518_REG_VIN3);
|
||||
|
||||
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_INT);
|
||||
data->beeps = 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->beep_enable = (gl518_read_value(client,GL518_REG_CONF) >> 2) & 1;
|
||||
|
||||
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] = nr?IN_FROM_REG(data->voltage_min[nr]):
|
||||
VDD_FROM_REG(data->voltage_min[nr]);
|
||||
results[1] = nr?IN_FROM_REG(data->voltage_max[nr]):
|
||||
VDD_FROM_REG(data->voltage_max[nr]);
|
||||
if ((data->revision == 0x00) && (nr != 3))
|
||||
results[2] = 0;
|
||||
else
|
||||
results[2] = nr?IN_FROM_REG(data->voltage[nr]):
|
||||
VDD_FROM_REG(data->voltage[nr]);
|
||||
*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] = nr?IN_TO_REG(results[1]):VDD_TO_REG(results[1]);
|
||||
old = data->voltage_max[nr] << 8;
|
||||
}
|
||||
if (*nrels_mag >= 1) {
|
||||
data->voltage_min[nr] = nr?IN_TO_REG(results[0]):VDD_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],
|
||||
DIV_FROM_REG(data->fan_div[nr]));
|
||||
results[1] = FAN_FROM_REG(data->fan[nr],DIV_FROM_REG(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],
|
||||
DIV_FROM_REG(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] = ALARMS_FROM_REG(data->alarms);
|
||||
*nrels_mag = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void gl518_beep(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] = BEEP_ENABLE_FROM_REG(data->beep_enable);
|
||||
results[1] = BEEPS_FROM_REG(data->beeps);
|
||||
*nrels_mag = 2;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
if (*nrels_mag >= 1) {
|
||||
data->beep_enable = BEEP_ENABLE_TO_REG(results[0]);
|
||||
gl518_write_value(client,GL518_REG_CONF,
|
||||
(gl518_read_value(client,GL518_REG_CONF) & 0xfb) |
|
||||
(data->beep_enable << 2));
|
||||
}
|
||||
if (*nrels_mag >= 2) {
|
||||
data->beeps = BEEPS_TO_REG(results[1]);
|
||||
gl518_write_value(client,GL518_REG_ALARM,data->beeps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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[0] = 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 */
|
||||
|
||||
-546
@@ -1,546 +0,0 @@
|
||||
/*
|
||||
i2c-dev.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
Copyright (c) 1995-1997, 1999 by Frodo Looijaard <frodol@dds.nl> and
|
||||
Simon Vogl
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/* Note that this is a complete rewrite of Simon Vogl's i2c-dev module.
|
||||
But I have used so much of his original code and ideas that it seems
|
||||
only fair to recognize him as co-author -- Frodo */
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/malloc.h>
|
||||
#include "compat.h"
|
||||
#include "i2c.h"
|
||||
#include "smbus.h"
|
||||
#include "isa.h"
|
||||
#include "sensors.h"
|
||||
#include "version.h"
|
||||
#include "i2c-dev.h"
|
||||
|
||||
#ifdef MODULE
|
||||
extern int init_module(void);
|
||||
extern int cleanup_module(void);
|
||||
#endif /* def MODULE */
|
||||
|
||||
/* struct file_operations changed too often in the 2.1 series for nice code */
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70))
|
||||
static loff_t i2cdev_lseek (struct file *file, loff_t offset, int origin);
|
||||
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56))
|
||||
static long long i2cdev_lseek (struct file *file, long long offset, int origin);
|
||||
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0))
|
||||
static long long i2cdev_llseek (struct inode *inode, struct file *file,
|
||||
long long offset, int origin);
|
||||
#else
|
||||
static int i2cdev_lseek (struct inode *inode, struct file *file, off_t offset,
|
||||
int origin);
|
||||
#endif
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70))
|
||||
static ssize_t i2cdev_read (struct file *file, char *buf, size_t count,
|
||||
loff_t *offset);
|
||||
static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count,
|
||||
loff_t *offset);
|
||||
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0))
|
||||
static long i2cdev_read (struct inode *inode, struct file *file, char *buf,
|
||||
unsigned long count);
|
||||
static long i2cdev_write (struct inode *inode, struct file *file,
|
||||
const char *buf, unsigned long offset);
|
||||
#else
|
||||
static int i2cdev_read(struct inode *inode, struct file *file, char *buf,
|
||||
int count);
|
||||
static int i2cdev_write(struct inode *inode, struct file *file,
|
||||
const char *buf, int count);
|
||||
#endif
|
||||
|
||||
static int i2cdev_ioctl (struct inode *inode, struct file *file,
|
||||
unsigned int cmd, unsigned long arg);
|
||||
static int i2cdev_open (struct inode *inode, struct file *file);
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31))
|
||||
static int i2cdev_release (struct inode *inode, struct file *file);
|
||||
#else
|
||||
static void i2cdev_release (struct inode *inode, struct file *file);
|
||||
#endif
|
||||
|
||||
|
||||
static int i2cdev_attach_adapter(struct i2c_adapter *adap);
|
||||
static int i2cdev_detach_client(struct i2c_client *client);
|
||||
static int i2cdev_command(struct i2c_client *client, unsigned int cmd,
|
||||
void *arg);
|
||||
static void i2cdev_inc_use(struct i2c_client *client);
|
||||
static void i2cdev_dec_use(struct i2c_client *client);
|
||||
|
||||
static int i2cdev_init(void);
|
||||
static int i2cdev_cleanup(void);
|
||||
|
||||
static struct file_operations i2cdev_fops = {
|
||||
i2cdev_lseek,
|
||||
i2cdev_read,
|
||||
i2cdev_write,
|
||||
NULL, /* i2cdev_readdir */
|
||||
NULL, /* i2cdev_select */
|
||||
i2cdev_ioctl,
|
||||
NULL, /* i2cdev_mmap */
|
||||
i2cdev_open,
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,118)
|
||||
NULL, /* i2cdev_flush */
|
||||
#endif
|
||||
i2cdev_release,
|
||||
};
|
||||
|
||||
#define I2CDEV_CLIENTS_MAX I2C_ADAP_MAX
|
||||
static struct i2c_client *i2cdev_clients[I2CDEV_CLIENTS_MAX];
|
||||
|
||||
static struct i2c_driver i2cdev_driver = {
|
||||
/* name */ "i2c-dev dummy driver",
|
||||
/* id */ I2C_DRIVERID_I2CDEV,
|
||||
/* flags */ DF_NOTIFY,
|
||||
/* attach_adapter */ &i2cdev_attach_adapter,
|
||||
/* detach_client */ &i2cdev_detach_client,
|
||||
/* command */ &i2cdev_command,
|
||||
/* inc_use */ &i2cdev_inc_use,
|
||||
/* dec_use */ &i2cdev_dec_use
|
||||
};
|
||||
|
||||
static struct i2c_client i2cdev_client_template = {
|
||||
/* name */ "I2C /dev entry",
|
||||
/* id */ 1,
|
||||
/* flags */ 0,
|
||||
/* addr */ -1,
|
||||
/* adapter */ NULL,
|
||||
/* driver */ &i2cdev_driver,
|
||||
/* data */ NULL
|
||||
};
|
||||
|
||||
static int i2cdev_initialized;
|
||||
|
||||
/* Note that the lseek function is called llseek in 2.1 kernels. But things
|
||||
are complicated enough as is. */
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70))
|
||||
loff_t i2cdev_lseek (struct file *file, loff_t offset, int origin)
|
||||
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56))
|
||||
long long i2cdev_lseek (struct file *file, long long offset, int origin)
|
||||
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0))
|
||||
long long i2cdev_llseek (struct inode *inode, struct file *file,
|
||||
long long offset, int origin)
|
||||
#else
|
||||
int i2cdev_lseek (struct inode *inode, struct file *file, off_t offset,
|
||||
int origin)
|
||||
#endif
|
||||
{
|
||||
#ifdef DEBUG
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56))
|
||||
struct inode *inode = file->f_dentry->d_inode;
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */
|
||||
printk("i2c_dev,o: i2c-%d lseek to %ld bytes relative to %d.\n",
|
||||
MINOR(inode->i_rdev),offset,origin);
|
||||
#endif /* DEBUG */
|
||||
return -ESPIPE;
|
||||
}
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70))
|
||||
static ssize_t i2cdev_read (struct file *file, char *buf, size_t count,
|
||||
loff_t *offset)
|
||||
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0))
|
||||
static long i2cdev_read (struct inode *inode, struct file *file, char *buf,
|
||||
unsigned long count)
|
||||
#else
|
||||
static int i2cdev_read(struct inode *inode, struct file *file, char *buf,
|
||||
int count)
|
||||
#endif
|
||||
{
|
||||
char *tmp;
|
||||
int ret;
|
||||
|
||||
#ifdef DEBUG
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70))
|
||||
struct inode *inode = file->f_dentry->d_inode;
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */
|
||||
#endif /* DEBUG */
|
||||
|
||||
struct i2c_client *client = (struct i2c_client *)file->private_data;
|
||||
|
||||
/* copy user space data to kernel space. */
|
||||
tmp = kmalloc(count,GFP_KERNEL);
|
||||
if (tmp==NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("i2c_dev,o: i2c-%d reading %d bytes.\n",MINOR(inode->i_rdev),count);
|
||||
#endif
|
||||
|
||||
ret = i2c_master_recv(client,tmp,count);
|
||||
copy_to_user(buf,tmp,count);
|
||||
kfree(tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70))
|
||||
static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count,
|
||||
loff_t *offset)
|
||||
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0))
|
||||
static long i2cdev_write (struct inode *inode, struct file *file,
|
||||
const char *buf, unsigned long offset)
|
||||
#else
|
||||
static int i2cdev_write(struct inode *inode, struct file *file,
|
||||
const char *buf, int count)
|
||||
#endif
|
||||
{
|
||||
int ret;
|
||||
char *tmp;
|
||||
struct i2c_client *client = (struct i2c_client *)file->private_data;
|
||||
|
||||
#ifdef DEBUG
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70))
|
||||
struct inode *inode = file->f_dentry->d_inode;
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */
|
||||
#endif /* DEBUG */
|
||||
|
||||
/* copy user space data to kernel space. */
|
||||
tmp = kmalloc(count,GFP_KERNEL);
|
||||
if (tmp==NULL)
|
||||
return -ENOMEM;
|
||||
copy_from_user(tmp,buf,count);
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("i2c_dev,o: i2c-%d writing %d bytes.\n",MINOR(inode->i_rdev),count);
|
||||
#endif
|
||||
ret = i2c_master_send(client,tmp,count);
|
||||
kfree(tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct i2c_client *client = (struct i2c_client *)file->private_data;
|
||||
struct i2c_smbus_data data_arg;
|
||||
union smbus_data temp;
|
||||
int ver,datasize,res;
|
||||
|
||||
#ifdef DEBUG
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70))
|
||||
struct inode *inode = file->f_dentry->d_inode;
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */
|
||||
printk("i2c_dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n",
|
||||
MINOR(inode->i_rdev),cmd, arg);
|
||||
#endif /* DEBUG */
|
||||
|
||||
switch ( cmd ) {
|
||||
case I2C_SLAVE:
|
||||
if (arg > 0x7f)
|
||||
return -EINVAL;
|
||||
client->addr = arg;
|
||||
return 0;
|
||||
case I2C_TENBIT:
|
||||
printk("i2c-dev.o: ioctl I2C_TENBIT not (yet) supported!\n");
|
||||
return -EINVAL;
|
||||
case I2C_SMBUS:
|
||||
if (! arg) {
|
||||
#ifdef DEBUG
|
||||
printk("i2c-dev.o: NULL argument pointer in ioctl I2C_SMBUS.\n");
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
if (verify_area(VERIFY_READ,(struct i2c_smbus_data *) arg,
|
||||
sizeof(struct i2c_smbus_data))) {
|
||||
#ifdef DEBUG
|
||||
printk("i2c-dev.o: invalid argument pointer (%ld) "
|
||||
"in IOCTL I2C_SMBUS.\n", arg);
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
copy_from_user(&data_arg,(struct i2c_smbus_data *) arg,
|
||||
sizeof(struct i2c_smbus_data));
|
||||
if ((data_arg.size != SMBUS_BYTE) &&
|
||||
(data_arg.size != SMBUS_QUICK) &&
|
||||
(data_arg.size != SMBUS_BYTE_DATA) &&
|
||||
(data_arg.size != SMBUS_WORD_DATA) &&
|
||||
(data_arg.size != SMBUS_PROC_CALL) &&
|
||||
(data_arg.size != SMBUS_BLOCK_DATA)) {
|
||||
#ifdef DEBUG
|
||||
printk("i2c-dev.o: size out of range (%x) in ioctl I2C_SMBUS.\n",
|
||||
data_arg.size);
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Note that SMBUS_READ and SMBUS_WRITE are 0 and 1, so the check is
|
||||
valid if size==SMBUS_QUICK too. */
|
||||
if ((data_arg.read_write != SMBUS_READ) &&
|
||||
(data_arg.read_write != SMBUS_WRITE)) {
|
||||
#ifdef DEBUG
|
||||
printk("i2c-dev.o: read_write out of range (%x) in ioctl I2C_SMBUS.\n",
|
||||
data_arg.read_write);
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Note that command values are always valid! */
|
||||
|
||||
if ((data_arg.size == SMBUS_QUICK) ||
|
||||
((data_arg.size == SMBUS_BYTE) &&
|
||||
(data_arg.read_write == SMBUS_WRITE)))
|
||||
/* These are special: we do not use data */
|
||||
return smbus_access(client->adapter, client->addr,
|
||||
data_arg.read_write, data_arg.command,
|
||||
data_arg.size, NULL);
|
||||
|
||||
if (data_arg.data == NULL) {
|
||||
#ifdef DEBUG
|
||||
printk("i2c-dev.o: data is NULL pointer in ioctl I2C_SMBUS.\n");
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* This seems unlogical but it is not: if the user wants to read a
|
||||
value, we must write that value to user memory! */
|
||||
ver = (data_arg.read_write == SMBUS_WRITE)?VERIFY_READ:VERIFY_WRITE;
|
||||
|
||||
if ((data_arg.size == SMBUS_BYTE_DATA) || (data_arg.size == SMBUS_BYTE))
|
||||
datasize = sizeof(data_arg.data->byte);
|
||||
else if (data_arg.size == SMBUS_WORD_DATA)
|
||||
datasize = sizeof(data_arg.data->word);
|
||||
else /* size == SMBUS_BLOCK_DATA */
|
||||
datasize = sizeof(data_arg.data->block);
|
||||
|
||||
if (verify_area(ver,data_arg.data,datasize)) {
|
||||
#ifdef DEBUG
|
||||
printk("i2c-dev.o: invalid pointer data (%p) in ioctl I2C_SMBUS.\n",
|
||||
data_arg.data);
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
if (data_arg.read_write == SMBUS_WRITE) {
|
||||
copy_from_user(&temp,data_arg.data,datasize);
|
||||
return smbus_access(client->adapter,client->addr,data_arg.read_write,
|
||||
data_arg.command,data_arg.size,&temp);
|
||||
} else {
|
||||
res = smbus_access(client->adapter,client->addr,data_arg.read_write,
|
||||
data_arg.command,data_arg.size,&temp);
|
||||
if (!res)
|
||||
copy_to_user(data_arg.data,&temp,datasize);
|
||||
return res;
|
||||
}
|
||||
default:
|
||||
return i2c_control(client,cmd,arg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2cdev_open (struct inode *inode, struct file *file)
|
||||
{
|
||||
unsigned int minor = MINOR(inode->i_rdev);
|
||||
struct i2c_client *client;
|
||||
|
||||
if (! i2cdev_clients[minor]) {
|
||||
#ifdef DEBUG
|
||||
printk("i2c-dev.o: Trying to open unattached adapter i2c-%d\n",minor);
|
||||
#endif
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Note that we here allocate a client for later use, but we will *not*
|
||||
register this client! Yes, this is safe. No, it is not very clean. */
|
||||
if(! (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
memcpy(client,&i2cdev_client_template,sizeof(struct i2c_client));
|
||||
client->adapter = i2cdev_clients[minor]->adapter;
|
||||
file->private_data = client;
|
||||
|
||||
MOD_INC_USE_COUNT;
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("i2c-dev.o: opened i2c-%d\n",minor);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31))
|
||||
static int i2cdev_release (struct inode *inode, struct file *file)
|
||||
#else
|
||||
static void i2cdev_release (struct inode *inode, struct file *file)
|
||||
#endif
|
||||
{
|
||||
kfree(file->private_data);
|
||||
file->private_data=NULL;
|
||||
#ifdef DEBUG
|
||||
printk("i2c-dev.o: Closed: i2c-%d\n", MINOR(inode->i_rdev));
|
||||
#endif
|
||||
MOD_DEC_USE_COUNT;
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31))
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int i2cdev_attach_adapter(struct i2c_adapter *adap)
|
||||
{
|
||||
int i,res;
|
||||
struct i2c_client *client;
|
||||
|
||||
if ((i = i2c_adapter_id(adap)) < 0) {
|
||||
printk("i2c-dev.o: Unknown adapter to attach?!?\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (i >= I2CDEV_CLIENTS_MAX) {
|
||||
printk("i2c-dev.o: Adapter number to attach too large?!? (%d)\n",i);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (i2cdev_clients[i]) {
|
||||
printk("i2c-dev.o: Adapter to attach already in use?!? (%d)\n",i);
|
||||
return -EBUSY;
|
||||
}
|
||||
if (i2c_is_isa_adapter(adap)) {
|
||||
#ifdef DEBUG
|
||||
printk("i2c-dev.o: Can't attach ISA adapter!\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (! (client = (struct i2c_client *)kmalloc(sizeof(struct i2c_client),
|
||||
GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
memcpy(client,&i2cdev_client_template,sizeof(struct i2c_client));
|
||||
client->adapter = adap;
|
||||
if ((res = i2c_attach_client(client))) {
|
||||
printk("i2c-dev.o: Attaching client failed.\n");
|
||||
kfree(client);
|
||||
return res;
|
||||
}
|
||||
i2cdev_clients[i] = client;
|
||||
printk("i2c-dev.o: Registered '%s' as minor %d\n",adap->name,i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2cdev_detach_client(struct i2c_client *client)
|
||||
{
|
||||
struct i2c_adapter *adap = client->adapter;
|
||||
int i,res;
|
||||
|
||||
if ((i = i2c_adapter_id(adap)) < 0) {
|
||||
printk("i2c-dev.o: Detaching unknown adapter?!?\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
if (i >= I2CDEV_CLIENTS_MAX) {
|
||||
printk("i2c-dev.o: Adapter number to detach too large?!? (%d)\n",i);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (!i2cdev_clients[i]) {
|
||||
printk("i2c-dev.o: Adapter to detach not in use?!? (%d)\n",i);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if ((res = i2c_detach_client(client))) {
|
||||
printk("i2c-dev.o: detaching client %d failed.\n",i);
|
||||
return res;
|
||||
}
|
||||
|
||||
kfree(i2cdev_clients[i]);
|
||||
i2cdev_clients[i] = NULL;
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("i2c-dev.o: Adapter unregistered: %s\n",adap->name);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2cdev_command(struct i2c_client *client, unsigned int cmd,
|
||||
void *arg)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void i2cdev_inc_use(struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_INC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void i2cdev_dec_use(struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_DEC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
int i2cdev_init(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
printk("i2c-dev.o version %s (%s)\n",LM_VERSION,LM_DATE);
|
||||
i2cdev_initialized = 0;
|
||||
if (register_chrdev(I2C_MAJOR,"i2c",&i2cdev_fops)) {
|
||||
printk("i2c-dev.o: unable to get major %d for i2c bus\n",I2C_MAJOR);
|
||||
return -EIO;
|
||||
}
|
||||
i2cdev_initialized ++;
|
||||
|
||||
if ((res = i2c_add_driver(&i2cdev_driver))) {
|
||||
printk("i2c-dev.o: Driver registration failed, module not inserted.\n");
|
||||
i2cdev_cleanup();
|
||||
return res;
|
||||
}
|
||||
i2cdev_initialized ++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2cdev_cleanup(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (i2cdev_initialized >= 2) {
|
||||
if ((res = i2c_del_driver(&i2cdev_driver))) {
|
||||
printk("i2c-dev.o: Driver deregistration failed, "
|
||||
"module not removed.\n");
|
||||
return res;
|
||||
}
|
||||
i2cdev_initialized ++;
|
||||
}
|
||||
|
||||
if (i2cdev_initialized >= 1) {
|
||||
if ((res = unregister_chrdev(I2C_MAJOR,"i2c"))) {
|
||||
printk("i2c-dev.o: unable to release major %d for i2c bus\n",I2C_MAJOR);
|
||||
return res;
|
||||
}
|
||||
i2cdev_initialized --;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MODULE
|
||||
|
||||
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and Simon G. Vogl <simon@tk.uni-linz.ac.at>");
|
||||
MODULE_DESCRIPTION("I2C /dev entries driver");
|
||||
|
||||
int init_module(void)
|
||||
{
|
||||
return i2cdev_init();
|
||||
}
|
||||
|
||||
int cleanup_module(void)
|
||||
{
|
||||
return i2cdev_cleanup();
|
||||
}
|
||||
|
||||
#endif /* def MODULE */
|
||||
|
||||
-158
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
i2c-dev.h - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef SENSORS_I2C_DEV_H
|
||||
#define SENSORS_I2C_DEV_H
|
||||
|
||||
#ifdef LM_SENSORS
|
||||
#include "i2c.h"
|
||||
#include "smbus.h"
|
||||
#else /* ndef LM_SENSORS */
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/smbus.h>
|
||||
#endif /* def LM_SENSORS */
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* Some IOCTL commands are defined in <linux/i2c.h> */
|
||||
/* Note: 10-bit addresses are NOT supported! */
|
||||
|
||||
#define I2C_SMBUS 0x0720
|
||||
|
||||
/* This is the structure as used in the I2C_SMBUS ioctl call */
|
||||
struct i2c_smbus_data {
|
||||
char read_write;
|
||||
__u8 command;
|
||||
int size;
|
||||
union smbus_data *data;
|
||||
};
|
||||
|
||||
#ifndef __KERNEL__
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
extern inline __s32 i2c_smbus_access(int file, char read_write, __u8 command,
|
||||
int size, union smbus_data *data)
|
||||
{
|
||||
struct i2c_smbus_data args;
|
||||
|
||||
args.read_write = read_write;
|
||||
args.command = command;
|
||||
args.size = size;
|
||||
args.data = data;
|
||||
return ioctl(file,I2C_SMBUS,&args);
|
||||
}
|
||||
|
||||
|
||||
extern inline __s32 i2c_smbus_write_quick(int file, __u8 value)
|
||||
{
|
||||
return i2c_smbus_access(file,value,0,SMBUS_QUICK,NULL);
|
||||
}
|
||||
|
||||
extern inline __s32 i2c_smbus_read_byte(int file)
|
||||
{
|
||||
union smbus_data data;
|
||||
if (i2c_smbus_access(file,SMBUS_READ,0,SMBUS_BYTE,&data))
|
||||
return -1;
|
||||
else
|
||||
return 0x0FF & data.byte;
|
||||
}
|
||||
|
||||
extern inline __s32 i2c_smbus_write_byte(int file, __u8 value)
|
||||
{
|
||||
return i2c_smbus_access(file,SMBUS_WRITE,value, SMBUS_BYTE,NULL);
|
||||
}
|
||||
|
||||
extern inline __s32 i2c_smbus_read_byte_data(int file, __u8 command)
|
||||
{
|
||||
union smbus_data data;
|
||||
if (i2c_smbus_access(file,SMBUS_READ,command,SMBUS_BYTE_DATA,&data))
|
||||
return -1;
|
||||
else
|
||||
return 0x0FF & data.byte;
|
||||
}
|
||||
|
||||
extern inline __s32 i2c_smbus_write_byte_data(int file, __u8 command,
|
||||
__u8 value)
|
||||
{
|
||||
union smbus_data data;
|
||||
data.byte = value;
|
||||
return i2c_smbus_access(file,SMBUS_WRITE,command,SMBUS_BYTE_DATA,&data);
|
||||
}
|
||||
|
||||
extern inline __s32 i2c_smbus_read_word_data(int file, __u8 command)
|
||||
{
|
||||
union smbus_data data;
|
||||
if (i2c_smbus_access(file,SMBUS_READ,command,SMBUS_WORD_DATA,&data))
|
||||
return -1;
|
||||
else
|
||||
return 0x0FFFF & data.word;
|
||||
}
|
||||
|
||||
extern inline __s32 i2c_smbus_write_word_data(int file, __u8 command,
|
||||
__u16 value)
|
||||
{
|
||||
union smbus_data data;
|
||||
data.word = value;
|
||||
return i2c_smbus_access(file,SMBUS_WRITE,command,SMBUS_WORD_DATA, &data);
|
||||
}
|
||||
|
||||
extern inline __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value)
|
||||
{
|
||||
union smbus_data data;
|
||||
data.word = value;
|
||||
if (i2c_smbus_access(file,SMBUS_WRITE,command,SMBUS_PROC_CALL,&data))
|
||||
return -1;
|
||||
else
|
||||
return 0x0FFFF & data.word;
|
||||
}
|
||||
|
||||
|
||||
/* Returns the number of read bytes */
|
||||
extern inline __s32 i2c_smbus_read_block_data(int file, __u8 command,
|
||||
__u8 *values)
|
||||
{
|
||||
union smbus_data data;
|
||||
int i;
|
||||
if (i2c_smbus_access(file,SMBUS_READ,command,SMBUS_BLOCK_DATA,&data))
|
||||
return -1;
|
||||
else {
|
||||
for (i = 1; i <= data.block[0]; i++)
|
||||
values[i-1] = data.block[i];
|
||||
return data.block[0];
|
||||
}
|
||||
}
|
||||
|
||||
extern inline __s32 i2c_smbus_write_block_data(int file, __u8 command,
|
||||
__u8 length, __u8 *values)
|
||||
{
|
||||
union smbus_data data;
|
||||
int i;
|
||||
if (length > 32)
|
||||
length = 32;
|
||||
for (i = 1; i <= length; i++)
|
||||
data.block[i] = values[i-1];
|
||||
data.block[0] = length;
|
||||
return i2c_smbus_access(file,SMBUS_WRITE,command,SMBUS_BLOCK_DATA,&data);
|
||||
}
|
||||
|
||||
#endif /* ndef __KERNEL__ */
|
||||
|
||||
#endif
|
||||
-492
@@ -1,492 +0,0 @@
|
||||
/*
|
||||
i2c-proc.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
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/proc_fs.h>
|
||||
|
||||
#include "i2c.h"
|
||||
#include "smbus.h"
|
||||
#include "isa.h"
|
||||
#include "version.h"
|
||||
#include "compat.h"
|
||||
#include "sensors.h"
|
||||
|
||||
#ifdef MODULE
|
||||
extern int init_module(void);
|
||||
extern int cleanup_module(void);
|
||||
#endif /* def MODULE */
|
||||
|
||||
static int i2cproc_init(void);
|
||||
static int i2cproc_cleanup(void);
|
||||
static int i2cproc_attach_adapter(struct i2c_adapter *adapter);
|
||||
static int i2cproc_detach_client(struct i2c_client *client);
|
||||
static int i2cproc_command(struct i2c_client *client, unsigned int cmd,
|
||||
void *arg);
|
||||
static void i2cproc_inc_use(struct i2c_client *client);
|
||||
static void i2cproc_dec_use(struct i2c_client *client);
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58))
|
||||
static void monitor_bus_i2c(struct inode *inode, int fill);
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
||||
|
||||
static ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count,
|
||||
loff_t *ppos);
|
||||
static int read_bus_i2c(char *buf, char **start, off_t offset, int len,
|
||||
int *eof , void *private);
|
||||
|
||||
#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */
|
||||
|
||||
static int i2cproc_bus_read(struct inode * inode, struct file * file,
|
||||
char * buf, int count);
|
||||
static int read_bus_i2c(char *buf, char **start, off_t offset, int len,
|
||||
int unused);
|
||||
|
||||
static struct proc_dir_entry proc_bus_dir =
|
||||
{
|
||||
/* low_ino */ 0, /* Set by proc_register_dynamic */
|
||||
/* namelen */ 3,
|
||||
/* name */ "bus",
|
||||
/* mode */ S_IRUGO | S_IXUGO | S_IFDIR,
|
||||
/* nlink */ 2, /* Corrected by proc_register[_dynamic] */
|
||||
/* uid */ 0,
|
||||
/* gid */ 0,
|
||||
/* size */ 0,
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,0,36))
|
||||
/* ops */ &proc_dir_inode_operations,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct proc_dir_entry proc_bus_i2c_dir =
|
||||
{
|
||||
/* low_ino */ 0, /* Set by proc_register_dynamic */
|
||||
/* namelen */ 3,
|
||||
/* name */ "i2c",
|
||||
/* mode */ S_IRUGO | S_IFREG,
|
||||
/* nlink */ 1,
|
||||
/* uid */ 0,
|
||||
/* gid */ 0,
|
||||
/* size */ 0,
|
||||
/* ops */ NULL,
|
||||
/* get_info */ &read_bus_i2c
|
||||
};
|
||||
|
||||
/* List of registered entries in /proc/bus */
|
||||
static struct proc_dir_entry *i2cproc_proc_entries[I2C_ADAP_MAX];
|
||||
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */
|
||||
|
||||
/* To implement the dynamic /proc/bus/i2c-? files, we need our own
|
||||
implementation of the read hook */
|
||||
static struct file_operations i2cproc_operations = {
|
||||
NULL,
|
||||
i2cproc_bus_read,
|
||||
};
|
||||
|
||||
static struct inode_operations i2cproc_inode_operations = {
|
||||
&i2cproc_operations
|
||||
};
|
||||
|
||||
|
||||
/* Used by init/cleanup */
|
||||
static int i2cproc_initialized;
|
||||
|
||||
/* This is a sorted list of all adapters that will have entries in /proc/bus */
|
||||
static struct i2c_adapter *i2cproc_adapters[I2C_ADAP_MAX];
|
||||
|
||||
/* Inodes of /dev/bus/i2c-? files */
|
||||
static int i2cproc_inodes[I2C_ADAP_MAX];
|
||||
|
||||
/* We will use a nasty trick: we register a driver, that will be notified
|
||||
for each adapter. Then, we register a dummy client on the adapter, that
|
||||
will get notified if the adapter is removed. This is the same trick as
|
||||
used in i2c/i2c-dev.c */
|
||||
static struct i2c_driver i2cproc_driver = {
|
||||
/* name */ "i2c-proc dummy driver",
|
||||
/* id */ I2C_DRIVERID_I2CPROC,
|
||||
/* flags */ DF_NOTIFY,
|
||||
/* attach_adapter */ &i2cproc_attach_adapter,
|
||||
/* detach_client */ &i2cproc_detach_client,
|
||||
/* command */ &i2cproc_command,
|
||||
/* inc_use */ &i2cproc_inc_use,
|
||||
/* dec_use */ &i2cproc_dec_use
|
||||
};
|
||||
|
||||
static struct i2c_client i2cproc_client_template = {
|
||||
/* name */ "i2c-proc dummy client",
|
||||
/* id */ 1,
|
||||
/* flags */ 0,
|
||||
/* addr */ -1,
|
||||
/* adapter */ NULL,
|
||||
/* driver */ &i2cproc_driver,
|
||||
/* data */ NULL
|
||||
};
|
||||
|
||||
|
||||
int i2cproc_init(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
||||
struct proc_dir_entry *proc_bus_i2c;
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */
|
||||
|
||||
printk("i2c-proc.o version %s (%s)\n",LM_VERSION,LM_DATE);
|
||||
i2cproc_initialized = 0;
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
||||
if (! proc_bus) {
|
||||
printk("i2c-proc.o: /proc/bus/ does not exist, module not inserted.\n");
|
||||
i2cproc_cleanup();
|
||||
return -ENOENT;
|
||||
}
|
||||
proc_bus_i2c = create_proc_entry("i2c",0,proc_bus);
|
||||
if (!proc_bus_i2c) {
|
||||
printk("i2c-proc.o: Could not create /proc/bus/i2c, "
|
||||
"module not inserted.\n");
|
||||
i2cproc_cleanup();
|
||||
return -ENOENT;
|
||||
}
|
||||
proc_bus_i2c->read_proc = &read_bus_i2c;
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58))
|
||||
proc_bus_i2c->fill_inode = &monitor_bus_i2c;
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */
|
||||
i2cproc_initialized += 2;
|
||||
#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */
|
||||
/* In Linux 2.0.x, there is no /proc/bus! But I hope no other module
|
||||
introduced it, or we are fucked. And 2.0.35 and earlier does not
|
||||
export proc_dir_inode_operations, so we grab it from proc_net,
|
||||
which also uses it. Not nice. */
|
||||
/* #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,0,36) */
|
||||
proc_bus_dir.ops = proc_net.ops;
|
||||
/* #endif */
|
||||
if ((res = proc_register_dynamic(&proc_root, &proc_bus_dir))) {
|
||||
printk("i2c-proc.o: Could not create /proc/bus/, module not inserted.\n");
|
||||
i2cproc_cleanup();
|
||||
return res;
|
||||
}
|
||||
i2cproc_initialized ++;
|
||||
if ((res = proc_register_dynamic(&proc_bus_dir, &proc_bus_i2c_dir))) {
|
||||
printk("i2c-proc.o: Could not create /proc/bus/i2c, "
|
||||
"module not inserted.\n");
|
||||
i2cproc_cleanup();
|
||||
return res;
|
||||
}
|
||||
i2cproc_initialized ++;
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */
|
||||
if ((res = i2c_add_driver(&i2cproc_driver))) {
|
||||
printk("i2c-proc.o: Driver registration failed, module not inserted.\n");
|
||||
i2cproc_cleanup();
|
||||
return res;
|
||||
}
|
||||
i2cproc_initialized ++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2cproc_cleanup(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (i2cproc_initialized >= 3) {
|
||||
if ((res = i2c_del_driver(&i2cproc_driver))) {
|
||||
printk("i2c-proc.o: Driver deregistration failed, "
|
||||
"module not removed.\n");
|
||||
return res;
|
||||
}
|
||||
i2cproc_initialized--;
|
||||
}
|
||||
if (i2cproc_initialized >= 1) {
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
||||
remove_proc_entry("i2c",proc_bus);
|
||||
i2cproc_initialized -= 2;
|
||||
#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */
|
||||
if (i2cproc_initialized >= 2) {
|
||||
if ((res = proc_unregister(&proc_bus_dir,proc_bus_i2c_dir.low_ino))) {
|
||||
printk("i2c-proc.o: could not delete /proc/bus/i2c, "
|
||||
"module not removed.");
|
||||
return res;
|
||||
}
|
||||
i2cproc_initialized --;
|
||||
}
|
||||
if ((res = proc_unregister(&proc_root,proc_bus_dir.low_ino))) {
|
||||
printk("i2c-proc.o: could not delete /proc/bus/, "
|
||||
"module not removed.");
|
||||
return res;
|
||||
}
|
||||
i2cproc_initialized --;
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58))
|
||||
/* Monitor access to /proc/bus/i2c*; make unloading i2c-proc.o impossible
|
||||
if some process still uses it or some file in it */
|
||||
void monitor_bus_i2c(struct inode *inode, int fill)
|
||||
{
|
||||
if (fill)
|
||||
MOD_INC_USE_COUNT;
|
||||
else
|
||||
MOD_DEC_USE_COUNT;
|
||||
}
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */
|
||||
|
||||
|
||||
/* This function generates the output for /proc/bus/i2c */
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
||||
int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof,
|
||||
void *private)
|
||||
#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */
|
||||
int read_bus_i2c(char *buf, char **start, off_t offset, int len, int unused)
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */
|
||||
{
|
||||
int i;
|
||||
len = 0;
|
||||
for (i = 0; i < I2C_ADAP_MAX; i++)
|
||||
if (i2cproc_adapters[i])
|
||||
len += sprintf(buf+len, "i2c-%d\t%s\t%-32s\t%-32s\n",
|
||||
i2c_adapter_id(i2cproc_adapters[i]),
|
||||
i2c_is_smbus_adapter(i2cproc_adapters[i])?"smbus":
|
||||
#ifdef DEBUG
|
||||
i2c_is_isa_adapter(i2cproc_adapters[i])?"isa":
|
||||
#endif /* def DEBUG */
|
||||
"i2c",
|
||||
i2cproc_adapters[i]->name,
|
||||
i2cproc_adapters[i]->algo->name);
|
||||
return len;
|
||||
}
|
||||
|
||||
/* This function generates the output for /proc/bus/i2c-? */
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
||||
ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct inode * inode = file->f_dentry->d_inode;
|
||||
#else (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29))
|
||||
int i2cproc_bus_read(struct inode * inode, struct file * file,char * buf,
|
||||
int count)
|
||||
{
|
||||
#endif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
||||
char *kbuf;
|
||||
struct i2c_client *client;
|
||||
int i,j,len=0;
|
||||
|
||||
if (count < 0)
|
||||
return -EINVAL;
|
||||
if (count > 4000)
|
||||
count = 4000;
|
||||
for (i = 0; i < I2C_ADAP_MAX; i++)
|
||||
if (i2cproc_inodes[i] == inode->i_ino) {
|
||||
if (! (kbuf = kmalloc(count,GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
for (j = 0; j < I2C_CLIENT_MAX; j++)
|
||||
if ((client = i2cproc_adapters[i]->clients[j]))
|
||||
/* Filter out dummy clients */
|
||||
#ifndef DEBUG
|
||||
if ((client->driver->id != I2C_DRIVERID_I2CPROC) &&
|
||||
(client->driver->id != I2C_DRIVERID_I2CDEV))
|
||||
#endif /* ndef DEBUG */
|
||||
len += sprintf(kbuf+len,"%x\t%-32s\t%-32s\n",
|
||||
#ifdef DEBUG
|
||||
i2c_is_isa_client(client)?
|
||||
((struct isa_client *) client)->isa_addr&0xffffff:
|
||||
#endif /* def DEBUG */
|
||||
client->addr,
|
||||
client->name,client->driver->name);
|
||||
if (file->f_pos+len > count)
|
||||
len = count - file->f_pos;
|
||||
len = len - file->f_pos;
|
||||
if (len < 0)
|
||||
len = 0;
|
||||
copy_to_user (buf,kbuf+file->f_pos,len);
|
||||
file->f_pos += len;
|
||||
kfree(kbuf);
|
||||
return len;
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
||||
/* We need to add the adapter to i2cproc_adapters, if it is interesting
|
||||
enough */
|
||||
int i2cproc_attach_adapter(struct i2c_adapter *adapter)
|
||||
{
|
||||
struct i2c_client *client;
|
||||
int i,res;
|
||||
char name[8];
|
||||
|
||||
struct proc_dir_entry *proc_entry;
|
||||
|
||||
#ifndef DEBUG
|
||||
if (i2c_is_isa_adapter(adapter))
|
||||
return 0;
|
||||
#endif /* ndef DEBUG */
|
||||
|
||||
for (i = 0; i < I2C_ADAP_MAX; i++)
|
||||
if(!i2cproc_adapters[i])
|
||||
break;
|
||||
if (i == I2C_ADAP_MAX) {
|
||||
printk("i2c-proc.o: Too many adapters!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
#ifndef DEBUG
|
||||
if (! (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) {
|
||||
#else /* def DEBUG */
|
||||
if (! (client = kmalloc(sizeof(struct isa_client),GFP_KERNEL))) {
|
||||
#endif
|
||||
printk("i2c-proc.o: Out of memory!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memcpy(client,&i2cproc_client_template,sizeof(struct i2c_client));
|
||||
#ifdef DEBUG
|
||||
((struct isa_client *) client) -> isa_addr = -1;
|
||||
#endif /* def DEBUG */
|
||||
client->adapter = adapter;
|
||||
if ((res = i2c_attach_client(client))) {
|
||||
printk("i2c-proc.o: Attaching client failed.\n");
|
||||
kfree(client);
|
||||
return res;
|
||||
}
|
||||
i2cproc_adapters[i] = adapter;
|
||||
|
||||
sprintf(name,"i2c-%d",i2c_adapter_id(adapter));
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
||||
proc_entry = create_proc_entry(name,0,proc_bus);
|
||||
if (! proc_entry) {
|
||||
printk("i2c-proc.o: Could not create /proc/bus/%s\n",name);
|
||||
kfree(client);
|
||||
return -ENOENT;
|
||||
}
|
||||
proc_entry->ops = &i2cproc_inode_operations;
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58))
|
||||
proc_entry->fill_inode = &monitor_bus_i2c;
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */
|
||||
#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */
|
||||
if (!(proc_entry = kmalloc(sizeof(struct proc_dir_entry)+strlen(name)+1,
|
||||
GFP_KERNEL))) {
|
||||
printk("i2c-proc.o: Out of memory!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(proc_entry,0,sizeof(struct proc_dir_entry));
|
||||
proc_entry->namelen = strlen(name);
|
||||
proc_entry->name = (char *) (proc_entry + 1);
|
||||
proc_entry->mode = S_IRUGO | S_IFREG;
|
||||
proc_entry->nlink = 1;
|
||||
proc_entry->ops = &i2cproc_inode_operations;
|
||||
strcpy((char *) proc_entry->name,name);
|
||||
|
||||
if ((res = proc_register_dynamic(&proc_bus_dir, proc_entry))) {
|
||||
printk("i2c-proc.o: Could not create %s.\n",name);
|
||||
kfree(proc_entry);
|
||||
kfree(client);
|
||||
return res;
|
||||
}
|
||||
|
||||
i2cproc_proc_entries[i] = proc_entry;
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */
|
||||
|
||||
i2cproc_inodes[i] = proc_entry->low_ino;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2cproc_detach_client(struct i2c_client *client)
|
||||
{
|
||||
int i,res;
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
||||
char name[8];
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */
|
||||
|
||||
#ifndef DEBUG
|
||||
if (i2c_is_isa_client(client))
|
||||
return 0;
|
||||
#endif /* ndef DEBUG */
|
||||
|
||||
for (i = 0; i < I2C_ADAP_MAX; i++)
|
||||
if (client->adapter == i2cproc_adapters[i]) {
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
||||
sprintf(name,"i2c-%d",i2c_adapter_id(i2cproc_adapters[i]));
|
||||
remove_proc_entry(name,proc_bus);
|
||||
#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */
|
||||
if ((res = proc_unregister(&proc_bus_dir,
|
||||
i2cproc_proc_entries[i]->low_ino))) {
|
||||
printk("i2c-proc.o: Deregistration of /proc entry failed, "
|
||||
"client not detached.\n");
|
||||
return res;
|
||||
}
|
||||
kfree(i2cproc_proc_entries[i]);
|
||||
i2cproc_proc_entries[i] = NULL;
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */
|
||||
if ((res = i2c_detach_client(client))) {
|
||||
printk("i2c-proc.o: Client deregistration failed, "
|
||||
"client not detached.\n");
|
||||
return res;
|
||||
}
|
||||
i2cproc_adapters[i] = NULL;
|
||||
i2cproc_inodes[i] = 0;
|
||||
kfree(client);
|
||||
return 0;
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* Nothing here yet */
|
||||
int i2cproc_command(struct i2c_client *client, unsigned int cmd,
|
||||
void *arg)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Nothing here yet */
|
||||
void i2cproc_inc_use(struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_INC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Nothing here yet */
|
||||
void i2cproc_dec_use(struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_DEC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MODULE
|
||||
|
||||
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
|
||||
MODULE_DESCRIPTION("I2C /proc/bus entries driver");
|
||||
|
||||
int init_module(void)
|
||||
{
|
||||
return i2cproc_init();
|
||||
}
|
||||
|
||||
int cleanup_module(void)
|
||||
{
|
||||
return i2cproc_cleanup();
|
||||
}
|
||||
|
||||
#endif /* def MODULE */
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
i2c.h - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
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.
|
||||
*/
|
||||
|
||||
|
||||
/* This is a stub files, that either includes <linux/i2c.h> (the kernel
|
||||
header) or "i2c/i2c.h" (the header in the i2c directory). */
|
||||
|
||||
#ifdef I2C
|
||||
#include "i2c/i2c.h"
|
||||
#else /* def I2C */
|
||||
#include <linux/i2c.h>
|
||||
#endif /* def I2C */
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
/*
|
||||
isa.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
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.
|
||||
*/
|
||||
|
||||
/* This implements an i2c algorithm/adapter for ISA bus. Not that this is
|
||||
on first sight very useful; almost no functionality is preserved.
|
||||
Except that it makes writing drivers for chips which can be on both
|
||||
the SMBus and the ISA bus very much easier. See lm78.c for an example
|
||||
of this. */
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "i2c.h"
|
||||
#ifdef SPINLOCK
|
||||
#include <asm/spinlock.h>
|
||||
#else
|
||||
#include <asm/semaphore.h>
|
||||
#endif
|
||||
|
||||
#include "version.h"
|
||||
#include "isa.h"
|
||||
|
||||
static int isa_master_xfer (struct isa_adapter *adap,
|
||||
struct i2c_msg msgs[], int num);
|
||||
static int isa_slave_send (struct isa_adapter *adap, char *data, int len);
|
||||
static int isa_slave_recv (struct isa_adapter *adap, char *data, int len);
|
||||
static int isa_algo_control (struct isa_adapter *adap, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
static int isa_client_register (struct isa_client *client);
|
||||
static int isa_client_unregister (struct isa_client *client);
|
||||
|
||||
static int isa_init(void);
|
||||
static int isa_cleanup(void);
|
||||
|
||||
#ifdef MODULE
|
||||
extern int init_module(void);
|
||||
extern int cleanup_module(void);
|
||||
#endif /* MODULE */
|
||||
|
||||
/* This is the actual algorithm we define */
|
||||
static struct isa_algorithm isa_algorithm = {
|
||||
/* name */ "ISA bus adapter",
|
||||
/* id */ ALGO_ISA,
|
||||
/* master_xfer */ &isa_master_xfer,
|
||||
/* slave_send */ &isa_slave_send,
|
||||
/* slave_rcv */ &isa_slave_recv,
|
||||
/* algo_control */ &isa_algo_control,
|
||||
/* client_register */ &isa_client_register,
|
||||
/* client_unregister*/&isa_client_unregister
|
||||
};
|
||||
|
||||
/* There can only be one... */
|
||||
static struct isa_adapter isa_adapter;
|
||||
|
||||
/* Used in isa_init/cleanup */
|
||||
static int isa_initialized;
|
||||
|
||||
/* Algorithm master_xfer call-back implementation. Can't do that... */
|
||||
int isa_master_xfer (struct isa_adapter *adap, struct i2c_msg msgs[],
|
||||
int num)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printk("isa.o: isa_master_xfer called for adapter `%s' "
|
||||
"(no i2c level access possible!)\n",
|
||||
adap->name);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Algorithm slave_send call-back implementation. Can't do that... */
|
||||
int isa_slave_send (struct isa_adapter *adap, char *data, int len)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printk("isa.o: isa_slave_send called for adapter `%s' "
|
||||
"(no i2c level access possible!)\n",
|
||||
adap->name);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Algorithm slave_recv call-back implementation. Can't do that... */
|
||||
int isa_slave_recv (struct isa_adapter *adap, char *data, int len)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printk("isa.o: isa_slave_recv called for adapter `%s' "
|
||||
"(no i2c level access possible!)\n",
|
||||
adap->name);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Here we can put additional calls to modify the workings of the algorithm.
|
||||
But right now, there is no need for that. */
|
||||
int isa_algo_control (struct isa_adapter *adap, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ehm... This is called when a client is registered to an adapter. We could
|
||||
do all kinds of neat stuff here like, ehm - returning success? */
|
||||
int isa_client_register (struct isa_client *client)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int isa_client_unregister (struct isa_client *client)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int isa_init(void)
|
||||
{
|
||||
int res;
|
||||
printk("isa.o version %s (%s)\n",LM_VERSION,LM_DATE);
|
||||
#ifdef DEBUG
|
||||
if (isa_initialized) {
|
||||
printk("isa.o: Oops, isa_init called a second time!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
#endif
|
||||
isa_initialized = 0;
|
||||
if ((res = isa_add_algorithm(&isa_algorithm))) {
|
||||
printk("isa.o: Algorithm registration failed, module not inserted.\n");
|
||||
isa_cleanup();
|
||||
return res;
|
||||
}
|
||||
isa_initialized++;
|
||||
strcpy(isa_adapter.name,"ISA main adapter");
|
||||
isa_adapter.id = ALGO_ISA | ISA_MAIN;
|
||||
isa_adapter.algo = &isa_algorithm;
|
||||
if ((res = isa_add_adapter(&isa_adapter))) {
|
||||
printk("isa.o: Adapter registration failed, "
|
||||
"module isa.o is not inserted\n.");
|
||||
isa_cleanup();
|
||||
return res;
|
||||
}
|
||||
isa_initialized++;
|
||||
printk("isa.o: ISA bus access for i2c modules initialized.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int isa_cleanup(void)
|
||||
{
|
||||
int res;
|
||||
if (isa_initialized >= 2)
|
||||
{
|
||||
if ((res = isa_del_adapter(&isa_adapter))) {
|
||||
printk("isa.o: Adapter deregistration failed, module not removed.\n");
|
||||
return res;
|
||||
} else
|
||||
isa_initialized--;
|
||||
}
|
||||
if (isa_initialized >= 1)
|
||||
{
|
||||
if ((res = isa_del_algorithm(&isa_algorithm))) {
|
||||
printk("isa.o: Algorithm deregistration failed, module not removed.\n");
|
||||
return res;
|
||||
} else
|
||||
isa_initialized--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef MODULE
|
||||
|
||||
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
|
||||
MODULE_DESCRIPTION("ISA bus access through i2c");
|
||||
|
||||
int init_module(void)
|
||||
{
|
||||
return isa_init();
|
||||
}
|
||||
|
||||
int cleanup_module(void)
|
||||
{
|
||||
return isa_cleanup();
|
||||
}
|
||||
|
||||
#endif /* MODULE */
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
isa.h - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef SENSORS_SENSOR_H
|
||||
#define SENSORS_SENSOR_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
/* This file must interface with Simon Vogl's i2c driver. Version 19981006 is
|
||||
OK, earlier versions are not; later versions will probably give problems
|
||||
too.
|
||||
*/
|
||||
#include <asm/types.h>
|
||||
|
||||
/* SPINLOCK is defined in i2c.h. */
|
||||
#ifdef SPINLOCK
|
||||
#include <asm/spinlock.h>
|
||||
#else
|
||||
#include <asm/semaphore.h>
|
||||
#endif
|
||||
|
||||
#ifdef LM_SENSORS
|
||||
#include "i2c.h"
|
||||
#else /* ndef LM_SENSORS */
|
||||
#include <linux/i2c.h>
|
||||
#endif /* def LM_SENSORS */
|
||||
|
||||
/* Note that this driver is *not* built upon smbus.c, but is parallel to it.
|
||||
We do not need SMBus facilities if we are on the ISA bus, after all */
|
||||
|
||||
/* Declarations, to keep the compiler happy */
|
||||
struct isa_driver;
|
||||
struct isa_client;
|
||||
struct isa_algorithm;
|
||||
struct isa_adapter;
|
||||
|
||||
/* A driver tells us how we should handle a specific kind of chip.
|
||||
A specific instance of such a chip is called a client.
|
||||
This structure is essentially the same as i2c_driver. */
|
||||
struct isa_driver {
|
||||
char name[32];
|
||||
int id;
|
||||
unsigned int flags;
|
||||
int (* attach_adapter) (struct isa_adapter *);
|
||||
int (* detach_client) (struct isa_client *);
|
||||
int (* command) (struct isa_client *, unsigned int cmd, void *arg);
|
||||
void (* inc_use) (struct isa_client *);
|
||||
void (* dec_use) (struct isa_client *);
|
||||
};
|
||||
|
||||
/* A client is a specifc instance of a chip: for each detected chip, there will
|
||||
be a client. Its operation is controlled by a driver.
|
||||
This structure is an extension of i2c_client. */
|
||||
struct isa_client {
|
||||
char name[32];
|
||||
int id;
|
||||
unsigned int flags;
|
||||
unsigned char addr;
|
||||
struct isa_adapter *adapter;
|
||||
struct isa_driver *driver;
|
||||
void *data;
|
||||
|
||||
/* Here ended i2c_client */
|
||||
unsigned int isa_addr;
|
||||
};
|
||||
|
||||
/* An algorithm describes how a certain class of busses can be accessed.
|
||||
A specific instance of sucj a bus is called an adapter.
|
||||
This structure is essentially the same as i2c_adapter. */
|
||||
struct isa_algorithm {
|
||||
char name[32];
|
||||
unsigned int id;
|
||||
int (* master_xfer) (struct isa_adapter *adap, struct i2c_msg msgs[],
|
||||
int num);
|
||||
int (* slave_send) (struct isa_adapter *,char *, int);
|
||||
int (* slave_recv) (struct isa_adapter *,char *, int);
|
||||
int (* algo_control) (struct isa_adapter *, unsigned int, unsigned long);
|
||||
int (* client_register) (struct isa_client *);
|
||||
int (* client_unregister) (struct isa_client *);
|
||||
};
|
||||
|
||||
/* An adapter is a specifc instance of a bus: for each detected bus, there will
|
||||
be an adapter. Its operation is controlled by an algorithm.
|
||||
SPINLOCK must be the same as declared in i2c.h.
|
||||
This structure is essentially the same as i2c_algorithm. */
|
||||
struct isa_adapter {
|
||||
char name[32];
|
||||
unsigned int id;
|
||||
struct isa_algorithm *algo;
|
||||
void *data;
|
||||
#ifdef SPINLOCK
|
||||
spinlock_t lock;
|
||||
unsigned long lockflags;
|
||||
#else
|
||||
struct semaphore lock;
|
||||
#endif
|
||||
unsigned int flags;
|
||||
struct isa_client *clients[I2C_CLIENT_MAX];
|
||||
int client_count;
|
||||
int timeout;
|
||||
int retries;
|
||||
};
|
||||
|
||||
|
||||
/* Detect whether we are on the isa bus. If this returns true, all i2c
|
||||
access will fail! */
|
||||
#define i2c_is_isa_client(clientptr) \
|
||||
((clientptr)->adapter->algo->id == ALGO_ISA)
|
||||
#define i2c_is_isa_adapter(adapptr) \
|
||||
((adapptr)->algo->id == ALGO_ISA)
|
||||
|
||||
/* Next: define ISA variants of registering. */
|
||||
#define isa_add_algorithm(algoptr) \
|
||||
i2c_add_algorithm((struct i2c_algorithm *) (algoptr))
|
||||
#define isa_del_algorithm(algoptr) \
|
||||
i2c_del_algorithm((struct i2c_algorithm *) (algoptr))
|
||||
|
||||
#define isa_add_adapter(adapptr) \
|
||||
i2c_add_adapter((struct i2c_adapter *) (adapptr))
|
||||
#define isa_del_adapter(adapptr) \
|
||||
i2c_del_adapter((struct i2c_adapter *) (adapptr))
|
||||
|
||||
#define isa_add_driver(driverptr) \
|
||||
i2c_add_driver((struct i2c_driver *) (driverptr))
|
||||
#define isa_del_driver(driverptr) \
|
||||
i2c_add_driver((struct i2c_driver *) (driverptr))
|
||||
|
||||
#define isa_attach_client(clientptr) \
|
||||
i2c_attach_client((struct i2c_client *) (clientptr))
|
||||
#define isa_detach_client(clientptr) \
|
||||
i2c_detach_client((struct i2c_client *) (clientptr))
|
||||
|
||||
#endif /* def __KERNEL__ */
|
||||
|
||||
/* We need to mark ISA algorithms in the algorithm structure. */
|
||||
#define ALGO_ISA 0x50000
|
||||
|
||||
/* ISA Adapter ids */
|
||||
#define ISA_MAIN 1
|
||||
|
||||
#endif /* ndef SENSORS_ISA_H */
|
||||
-363
@@ -1,363 +0,0 @@
|
||||
/*
|
||||
lm75.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
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 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
|
||||
|
||||
/* Conversions */
|
||||
#define TEMP_FROM_REG(val) (((val) >> 7) * 5)
|
||||
#define TEMP_TO_REG(val) (((((val) + 2) / 5) << 7) & 0xff80)
|
||||
|
||||
/* Initial values */
|
||||
#define LM75_INIT_TEMP_OS 600
|
||||
#define LM75_INIT_TEMP_HYST 500
|
||||
|
||||
/* Each client has this additional data */
|
||||
struct lm75_data {
|
||||
int sysctl_id;
|
||||
|
||||
struct semaphore update_lock;
|
||||
char valid; /* !=0 if following fields are valid */
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
|
||||
u16 temp,temp_os,temp_hyst; /* Register values */
|
||||
};
|
||||
|
||||
#ifdef MODULE
|
||||
extern int init_module(void);
|
||||
extern int cleanup_module(void);
|
||||
#endif /* MODULE */
|
||||
|
||||
static int lm75_init(void);
|
||||
static int lm75_cleanup(void);
|
||||
static int lm75_attach_adapter(struct i2c_adapter *adapter);
|
||||
static int lm75_detach_client(struct i2c_client *client);
|
||||
static int lm75_command(struct i2c_client *client, unsigned int cmd,
|
||||
void *arg);
|
||||
static void lm75_inc_use (struct i2c_client *client);
|
||||
static void lm75_dec_use (struct i2c_client *client);
|
||||
static u16 swap_bytes(u16 val);
|
||||
static int lm75_read_value(struct i2c_client *client, u8 reg);
|
||||
static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value);
|
||||
static void lm75_temp(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void lm75_update_client(struct i2c_client *client);
|
||||
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver lm75_driver = {
|
||||
/* name */ "LM75 sensor chip driver",
|
||||
/* id */ I2C_DRIVERID_LM75,
|
||||
/* flags */ DF_NOTIFY,
|
||||
/* attach_adapter */ &lm75_attach_adapter,
|
||||
/* detach_client */ &lm75_detach_client,
|
||||
/* command */ &lm75_command,
|
||||
/* inc_use */ &lm75_inc_use,
|
||||
/* dec_use */ &lm75_dec_use
|
||||
};
|
||||
|
||||
/* These files are created for each detected LM75. This is just a template;
|
||||
though at first sight, you might think we could use a statically
|
||||
allocated list, we need some way to get back to the parent - which
|
||||
is done through one of the 'extra' fields which are initialized
|
||||
when a new copy is allocated. */
|
||||
static ctl_table lm75_dir_table_template[] = {
|
||||
{ LM75_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm75_temp },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
/* Used by init/cleanup */
|
||||
static int lm75_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_LM75_NR 8
|
||||
static struct i2c_client *lm75_list[MAX_LM75_NR];
|
||||
|
||||
|
||||
int lm75_attach_adapter(struct i2c_adapter *adapter)
|
||||
{
|
||||
int address,err,i;
|
||||
struct i2c_client *new_client;
|
||||
struct lm75_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 = 0x48; (! err) && (address <= 0x4f); address ++) {
|
||||
|
||||
/* Later on, we will keep a list of registered addresses for each
|
||||
adapter, and check whether they are used here */
|
||||
|
||||
if (smbus_read_byte_data(adapter,address,LM75_REG_CONF) < 0)
|
||||
continue;
|
||||
|
||||
/* Real detection code goes here */
|
||||
|
||||
/* Allocate space for a new client structure */
|
||||
if (! (new_client = kmalloc(sizeof(struct i2c_client) +
|
||||
sizeof(struct lm75_data),
|
||||
GFP_KERNEL))) {
|
||||
err = -ENOMEM;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Find a place in our global list */
|
||||
for (i = 0; i < MAX_LM75_NR; i++)
|
||||
if (! lm75_list[i])
|
||||
break;
|
||||
if (i == MAX_LM75_NR) {
|
||||
err = -ENOMEM;
|
||||
printk("lm75.o: No empty slots left, recompile and heighten "
|
||||
"MAX_LM75_NR!\n");
|
||||
goto ERROR1;
|
||||
}
|
||||
lm75_list[i] = new_client;
|
||||
|
||||
/* Fill the new client structure with data */
|
||||
data = (struct lm75_data *) (new_client + 1);
|
||||
new_client->data = data;
|
||||
new_client->id = i;
|
||||
new_client->addr = address;
|
||||
new_client->adapter = adapter;
|
||||
new_client->driver = &lm75_driver;
|
||||
strcpy(new_client->name,"LM75 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,"lm75",
|
||||
lm75_dir_table_template)) < 0)
|
||||
goto ERROR3;
|
||||
data->sysctl_id = err;
|
||||
err = 0;
|
||||
|
||||
/* Initialize the LM75 chip */
|
||||
lm75_write_value(new_client,LM75_REG_TEMP_OS,
|
||||
TEMP_TO_REG(LM75_INIT_TEMP_OS));
|
||||
lm75_write_value(new_client,LM75_REG_TEMP_HYST,
|
||||
TEMP_TO_REG(LM75_INIT_TEMP_HYST));
|
||||
lm75_write_value(new_client,LM75_REG_CONF,0);
|
||||
|
||||
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:
|
||||
lm75_list[i] = NULL;
|
||||
ERROR1:
|
||||
kfree(new_client);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int lm75_detach_client(struct i2c_client *client)
|
||||
{
|
||||
int err,i;
|
||||
for (i = 0; i < MAX_LM75_NR; i++)
|
||||
if (client == lm75_list[i])
|
||||
break;
|
||||
if ((i == MAX_LM75_NR)) {
|
||||
printk("lm75.o: Client to detach not found.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
sensors_deregister_entry(((struct lm75_data *)(client->data))->sysctl_id);
|
||||
|
||||
if ((err = i2c_detach_client(client))) {
|
||||
printk("lm75.o: Client deregistration failed, client not detached.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
lm75_list[i] = NULL;
|
||||
kfree(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* No commands defined yet */
|
||||
int lm75_command(struct i2c_client *client, unsigned int cmd, void *arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Nothing here yet */
|
||||
void lm75_inc_use (struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_INC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Nothing here yet */
|
||||
void lm75_dec_use (struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_DEC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
u16 swap_bytes(u16 val)
|
||||
{
|
||||
return (val >> 8) | (val << 8);
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
int lm75_read_value(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
if (reg == LM75_REG_CONF)
|
||||
return smbus_read_byte_data(client->adapter,client->addr,reg);
|
||||
else
|
||||
return swap_bytes(smbus_read_word_data(client->adapter,client->addr,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. */
|
||||
int lm75_write_value(struct i2c_client *client, u8 reg, u16 value)
|
||||
{
|
||||
if (reg == LM75_REG_CONF)
|
||||
return smbus_write_byte_data(client->adapter,client->addr,reg,value);
|
||||
else
|
||||
return smbus_write_word_data(client->adapter,client->addr,reg,
|
||||
swap_bytes(value));
|
||||
}
|
||||
|
||||
void lm75_update_client(struct i2c_client *client)
|
||||
{
|
||||
struct lm75_data *data = client->data;
|
||||
|
||||
down(&data->update_lock);
|
||||
|
||||
if ((jiffies - data->last_updated > HZ+HZ/2 ) ||
|
||||
(jiffies < data->last_updated) || ! data->valid) {
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("Starting lm75 update\n");
|
||||
#endif
|
||||
|
||||
data->temp = lm75_read_value(client,LM75_REG_TEMP);
|
||||
data->temp_os = lm75_read_value(client,LM75_REG_TEMP_OS);
|
||||
data->temp_hyst = lm75_read_value(client,LM75_REG_TEMP_HYST);
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
up(&data->update_lock);
|
||||
}
|
||||
|
||||
|
||||
void lm75_temp(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct lm75_data *data = client->data;
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 1;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
lm75_update_client(client);
|
||||
results[0] = TEMP_FROM_REG(data->temp_os);
|
||||
results[1] = TEMP_FROM_REG(data->temp_hyst);
|
||||
results[2] = TEMP_FROM_REG(data->temp);
|
||||
*nrels_mag = 3;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
if (*nrels_mag >= 1) {
|
||||
data->temp_os = TEMP_TO_REG(results[0]);
|
||||
lm75_write_value(client,LM75_REG_TEMP_OS,data->temp_os);
|
||||
}
|
||||
if (*nrels_mag >= 2) {
|
||||
data->temp_os = TEMP_TO_REG(results[1]);
|
||||
lm75_write_value(client,LM75_REG_TEMP_HYST,data->temp_os);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int lm75_init(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
printk("lm75.o version %s (%s)\n",LM_VERSION,LM_DATE);
|
||||
lm75_initialized = 0;
|
||||
if ((res = i2c_add_driver(&lm75_driver))) {
|
||||
printk("lm75.o: Driver registration failed, module not inserted.\n");
|
||||
lm75_cleanup();
|
||||
return res;
|
||||
}
|
||||
lm75_initialized ++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lm75_cleanup(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (lm75_initialized >= 1) {
|
||||
if ((res = i2c_del_driver(&lm75_driver))) {
|
||||
printk("lm75.o: Driver deregistration failed, module not removed.\n");
|
||||
return res;
|
||||
}
|
||||
lm75_initialized --;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MODULE
|
||||
|
||||
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
|
||||
MODULE_DESCRIPTION("LM75 driver");
|
||||
|
||||
int init_module(void)
|
||||
{
|
||||
return lm75_init();
|
||||
}
|
||||
|
||||
int cleanup_module(void)
|
||||
{
|
||||
return lm75_cleanup();
|
||||
}
|
||||
|
||||
#endif /* MODULE */
|
||||
|
||||
-926
@@ -1,926 +0,0 @@
|
||||
/*
|
||||
lm78.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
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 <linux/proc_fs.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/types.h>
|
||||
#include "smbus.h"
|
||||
#include "version.h"
|
||||
#include "isa.h"
|
||||
#include "sensors.h"
|
||||
#include "i2c.h"
|
||||
#include "compat.h"
|
||||
|
||||
/* 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) (0x3a + (nr))
|
||||
#define LM78_REG_FAN(nr) (0x27 + (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
|
||||
|
||||
|
||||
/* Conversions. Rounding is only done on the TO_REG variants. */
|
||||
#define IN_TO_REG(val) (((val) * 10 + 8)/16)
|
||||
#define IN_FROM_REG(val) (((val) * 16) / 10)
|
||||
|
||||
#define FAN_TO_REG(val,div) ((val)==0?255:((1350000+(val)*(div)/2)/\
|
||||
((val)*(div))) & 0xff)
|
||||
#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))
|
||||
|
||||
#define TEMP_TO_REG(val) (((val)<0?(((val)-5)/10)&0xff:((val)+5)/10) & 0xff)
|
||||
#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10)
|
||||
|
||||
#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\
|
||||
(val)>=0x06?0:205-(val)*5)
|
||||
#define ALARMS_FROM_REG(val) (val)
|
||||
|
||||
#define DIV_FROM_REG(val) (1 << (val))
|
||||
#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
|
||||
|
||||
/* Initial limits. To keep them sane, we use the 'standard' translation as
|
||||
specified in the LM78 sheet. Use the config file to set better limits. */
|
||||
#define LM78_INIT_IN_0 (vid==350?280:vid)
|
||||
#define LM78_INIT_IN_1 (vid==350?280:vid)
|
||||
#define LM78_INIT_IN_2 330
|
||||
#define LM78_INIT_IN_3 (((500) * 100)/168)
|
||||
#define LM78_INIT_IN_4 (((1200) * 10)/38)
|
||||
#define LM78_INIT_IN_5 (((-1200) * -604)/2100)
|
||||
#define LM78_INIT_IN_6 (((-500) * -604)/909)
|
||||
|
||||
#define LM78_INIT_IN_PERCENTAGE 10
|
||||
|
||||
#define LM78_INIT_IN_MIN_0 \
|
||||
(LM78_INIT_IN_0 - LM78_INIT_IN_0 * LM78_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM78_INIT_IN_MAX_0 \
|
||||
(LM78_INIT_IN_0 + LM78_INIT_IN_0 * LM78_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM78_INIT_IN_MIN_1 \
|
||||
(LM78_INIT_IN_1 - LM78_INIT_IN_1 * LM78_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM78_INIT_IN_MAX_1 \
|
||||
(LM78_INIT_IN_1 + LM78_INIT_IN_1 * LM78_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM78_INIT_IN_MIN_2 \
|
||||
(LM78_INIT_IN_2 - LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM78_INIT_IN_MAX_2 \
|
||||
(LM78_INIT_IN_2 + LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM78_INIT_IN_MIN_3 \
|
||||
(LM78_INIT_IN_3 - LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM78_INIT_IN_MAX_3 \
|
||||
(LM78_INIT_IN_3 + LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM78_INIT_IN_MIN_4 \
|
||||
(LM78_INIT_IN_4 - LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM78_INIT_IN_MAX_4 \
|
||||
(LM78_INIT_IN_4 + LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM78_INIT_IN_MIN_5 \
|
||||
(LM78_INIT_IN_5 - LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM78_INIT_IN_MAX_5 \
|
||||
(LM78_INIT_IN_5 + LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM78_INIT_IN_MIN_6 \
|
||||
(LM78_INIT_IN_6 - LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM78_INIT_IN_MAX_6 \
|
||||
(LM78_INIT_IN_6 + LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100)
|
||||
|
||||
#define LM78_INIT_FAN_MIN_1 3000
|
||||
#define LM78_INIT_FAN_MIN_2 3000
|
||||
#define LM78_INIT_FAN_MIN_3 3000
|
||||
|
||||
#define LM78_INIT_TEMP_OVER 600
|
||||
#define LM78_INIT_TEMP_HYST 500
|
||||
|
||||
#ifdef MODULE
|
||||
extern int init_module(void);
|
||||
extern int cleanup_module(void);
|
||||
#endif /* MODULE */
|
||||
|
||||
/* 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. */
|
||||
|
||||
/* Types of chips supported */
|
||||
enum lm78_type { lm78, lm78j, lm79 };
|
||||
|
||||
/* 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 semaphore lock;
|
||||
int sysctl_id;
|
||||
enum lm78_type 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 */
|
||||
u8 temp; /* Register value */
|
||||
u8 temp_over; /* Register value */
|
||||
u8 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_init(void);
|
||||
static int lm78_cleanup(void);
|
||||
|
||||
static int lm78_attach_adapter(struct i2c_adapter *adapter);
|
||||
static int lm78_detect_isa(struct isa_adapter *adapter);
|
||||
static int lm78_detect_smbus(struct i2c_adapter *adapter);
|
||||
static int lm78_detach_client(struct i2c_client *client);
|
||||
static int lm78_detach_isa(struct isa_client *client);
|
||||
static int lm78_detach_smbus(struct i2c_client *client);
|
||||
static int lm78_new_client(struct i2c_adapter *adapter,
|
||||
struct i2c_client *new_client);
|
||||
static void lm78_remove_client(struct i2c_client *client);
|
||||
static int lm78_command(struct i2c_client *client, unsigned int cmd,
|
||||
void *arg);
|
||||
static void lm78_inc_use (struct i2c_client *client);
|
||||
static void lm78_dec_use (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 void lm78_update_client(struct i2c_client *client);
|
||||
static void lm78_init_client(struct i2c_client *client);
|
||||
|
||||
|
||||
static void lm78_in(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void lm78_fan(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void lm78_temp(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void lm78_vid(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void lm78_alarms(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void lm78_fan_div(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
|
||||
/* 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_LM78_NR 4
|
||||
static struct i2c_client *lm78_list[MAX_LM78_NR];
|
||||
|
||||
/* 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 lm78_driver = {
|
||||
/* name */ "LM78(-J) and LM79 sensor driver",
|
||||
/* id */ I2C_DRIVERID_LM78,
|
||||
/* flags */ DF_NOTIFY,
|
||||
/* attach_adapter */ &lm78_attach_adapter,
|
||||
/* detach_client */ &lm78_detach_client,
|
||||
/* command */ &lm78_command,
|
||||
/* inc_use */ &lm78_inc_use,
|
||||
/* dec_use */ &lm78_dec_use
|
||||
};
|
||||
|
||||
/* Used by lm78_init/cleanup */
|
||||
static int lm78_initialized = 0;
|
||||
|
||||
/* The /proc/sys entries */
|
||||
/* These files are created for each detected LM78. 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 lm78_dir_table_template[] = {
|
||||
{ LM78_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm78_in },
|
||||
{ LM78_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm78_in },
|
||||
{ LM78_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm78_in },
|
||||
{ LM78_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm78_in },
|
||||
{ LM78_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm78_in },
|
||||
{ LM78_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm78_in },
|
||||
{ LM78_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm78_in },
|
||||
{ LM78_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm78_fan },
|
||||
{ LM78_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm78_fan },
|
||||
{ LM78_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm78_fan },
|
||||
{ LM78_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm78_temp },
|
||||
{ LM78_SYSCTL_VID, "vid", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm78_vid },
|
||||
{ LM78_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm78_fan_div },
|
||||
{ LM78_SYSCTL_ALARMS, "alarms", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm78_alarms },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
||||
/* 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) */
|
||||
int lm78_attach_adapter(struct i2c_adapter *adapter)
|
||||
{
|
||||
if (i2c_is_isa_adapter(adapter))
|
||||
return lm78_detect_isa((struct isa_adapter *) adapter);
|
||||
else
|
||||
return lm78_detect_smbus(adapter);
|
||||
}
|
||||
|
||||
/* This function is called whenever a client should be removed:
|
||||
* lm78_driver is removed (when this module is unloaded)
|
||||
* when an adapter is removed which has a lm78 client (and lm78_driver
|
||||
is still present). */
|
||||
int lm78_detach_client(struct i2c_client *client)
|
||||
{
|
||||
if (i2c_is_isa_client(client))
|
||||
return lm78_detach_isa((struct isa_client *) client);
|
||||
else
|
||||
return lm78_detach_smbus(client);
|
||||
}
|
||||
|
||||
/* Detect whether there is a LM78 on the ISA bus, register and initialize
|
||||
it. */
|
||||
int lm78_detect_isa(struct isa_adapter *adapter)
|
||||
{
|
||||
int address,err;
|
||||
struct isa_client *new_client;
|
||||
enum lm78_type type;
|
||||
const char *type_name;
|
||||
const char *client_name;
|
||||
|
||||
/* OK, this is no detection. I know. It will do for now, though. */
|
||||
|
||||
err = 0;
|
||||
for (address = 0x290; (! err) && (address <= 0x290); address += 0x08) {
|
||||
if (check_region(address, LM78_EXTENT))
|
||||
continue;
|
||||
|
||||
/* Awful, but true: unused port addresses should return 0xff */
|
||||
if ((inb_p(address + 1) != 0xff) || (inb_p(address + 2) != 0xff) ||
|
||||
(inb_p(address + 3) != 0xff) || (inb_p(address + 7) != 0xff))
|
||||
continue;
|
||||
|
||||
if (inb_p(address + LM78_ADDR_REG_OFFSET) == 0xff) {
|
||||
outb_p(0x00,address + LM78_ADDR_REG_OFFSET);
|
||||
if (inb_p(address + LM78_ADDR_REG_OFFSET) == 0xff)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Real detection code goes here */
|
||||
|
||||
/* Determine exact type */
|
||||
outb_p(LM78_REG_CHIPID,address + LM78_ADDR_REG_OFFSET);
|
||||
err = inb_p(address + LM78_DATA_REG_OFFSET) & 0xfe;
|
||||
if (err == 0x00) {
|
||||
type = lm78;
|
||||
type_name = "lm78";
|
||||
client_name = "LM78 chip";
|
||||
} else if (err == 0x40) {
|
||||
type = lm78j;
|
||||
type_name = "lm78-j";
|
||||
client_name = "LM78-J chip";
|
||||
} else if (err == 0xc0) {
|
||||
type = lm79;
|
||||
type_name = "lm79";
|
||||
client_name = "LM79 chip";
|
||||
} else if (err == 0x02) {
|
||||
printk("lm78.o: Warning: Winbond W83781D detected (treated as a LM79)\n");
|
||||
type = lm79;
|
||||
type_name = "lm79";
|
||||
client_name = "LM79 chip";
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
printk("lm78.o: warning: probed non-lm78 chip?!? (%x)\n",err);
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
request_region(address, LM78_EXTENT, type_name);
|
||||
|
||||
/* Allocate space for a new client structure */
|
||||
if (! (new_client = kmalloc(sizeof(struct isa_client) +
|
||||
sizeof(struct lm78_data),
|
||||
GFP_KERNEL)))
|
||||
{
|
||||
err=-ENOMEM;
|
||||
goto ERROR1;
|
||||
}
|
||||
|
||||
/* Fill the new client structure with data */
|
||||
new_client->data = (struct lm78_data *) (new_client + 1);
|
||||
new_client->addr = 0;
|
||||
strcpy(new_client->name,client_name);
|
||||
((struct lm78_data *) (new_client->data))->type = type;
|
||||
new_client->isa_addr = address;
|
||||
if ((err = lm78_new_client((struct i2c_adapter *) adapter,
|
||||
(struct i2c_client *) new_client)))
|
||||
goto ERROR2;
|
||||
|
||||
/* Tell i2c-core a new client has arrived */
|
||||
if ((err = isa_attach_client(new_client)))
|
||||
goto ERROR3;
|
||||
|
||||
/* Register a new directory entry with module sensors */
|
||||
if ((err = sensors_register_entry((struct i2c_client *) new_client,
|
||||
type_name,
|
||||
lm78_dir_table_template)) < 0)
|
||||
goto ERROR4;
|
||||
((struct lm78_data *) (new_client->data)) -> sysctl_id = err;
|
||||
err = 0;
|
||||
|
||||
/* Initialize the LM78 chip */
|
||||
lm78_init_client((struct i2c_client *) new_client);
|
||||
continue;
|
||||
|
||||
/* OK, this is not exactly good programming practice, usually. But it is
|
||||
very code-efficient in this case. */
|
||||
|
||||
ERROR4:
|
||||
isa_detach_client(new_client);
|
||||
ERROR3:
|
||||
lm78_remove_client((struct i2c_client *) new_client);
|
||||
ERROR2:
|
||||
kfree(new_client);
|
||||
ERROR1:
|
||||
release_region(address, LM78_EXTENT);
|
||||
}
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
/* Deregister and remove a LM78 client */
|
||||
int lm78_detach_isa(struct isa_client *client)
|
||||
{
|
||||
int err,i;
|
||||
for (i = 0; i < MAX_LM78_NR; i++)
|
||||
if ((client == (struct isa_client *) (lm78_list[i])))
|
||||
break;
|
||||
if (i == MAX_LM78_NR) {
|
||||
printk("lm78.o: Client to detach not found.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
sensors_deregister_entry(((struct lm78_data *)(client->data))->sysctl_id);
|
||||
|
||||
if ((err = isa_detach_client(client))) {
|
||||
printk("lm78.o: Client deregistration failed, client not detached.\n");
|
||||
return err;
|
||||
}
|
||||
lm78_remove_client((struct i2c_client *) client);
|
||||
release_region(client->isa_addr,LM78_EXTENT);
|
||||
kfree(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lm78_detect_smbus(struct i2c_adapter *adapter)
|
||||
{
|
||||
int address,err;
|
||||
struct i2c_client *new_client;
|
||||
enum lm78_type type;
|
||||
const char *type_name,*client_name;
|
||||
|
||||
/* OK, this is no detection. I know. It will do for now, though. */
|
||||
err = 0;
|
||||
for (address = 0x20; (! err) && (address <= 0x2f); address ++) {
|
||||
|
||||
/* Later on, we will keep a list of registered addresses for each
|
||||
adapter, and check whether they are used here */
|
||||
|
||||
if (smbus_read_byte_data(adapter,address,LM78_REG_CONFIG) < 0)
|
||||
continue;
|
||||
|
||||
/* Real detection code goes here */
|
||||
|
||||
/* Determine exact type */
|
||||
err = smbus_read_byte_data(adapter,address,LM78_REG_CHIPID) & 0xfe;
|
||||
if (err == 0x00) {
|
||||
type = lm78;
|
||||
type_name = "lm78";
|
||||
client_name = "LM78 chip";
|
||||
} else if (err == 0x40) {
|
||||
type = lm78j;
|
||||
type_name = "lm78-j";
|
||||
client_name = "LM78-J chip";
|
||||
} else if (err == 0xc0) {
|
||||
type = lm79;
|
||||
type_name = "lm79";
|
||||
client_name = "LM79 chip";
|
||||
} else if (err == 0x02) {
|
||||
printk("lm78.o: Warning: Winbond W83781D detected (treated as a LM79)\n");
|
||||
type = lm79;
|
||||
type_name = "lm79";
|
||||
client_name = "LM79 chip";
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
printk("lm78.o: warning: probed non-lm78 chip?!? (%x)\n",err);
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/* Allocate space for a new client structure. To counter memory
|
||||
ragmentation somewhat, we only do one kmalloc. */
|
||||
if (! (new_client = kmalloc(sizeof(struct i2c_client) +
|
||||
sizeof(struct lm78_data),
|
||||
GFP_KERNEL))) {
|
||||
err = -ENOMEM;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Fill the new client structure with data */
|
||||
new_client->data = (struct lm78_data *) (new_client + 1);
|
||||
new_client->addr = address;
|
||||
strcpy(new_client->name,client_name);
|
||||
((struct lm78_data *) (new_client->data))->type = type;
|
||||
if ((err = lm78_new_client(adapter,new_client)))
|
||||
goto ERROR2;
|
||||
|
||||
/* Tell i2c-core a new client has arrived */
|
||||
if ((err = i2c_attach_client(new_client)))
|
||||
goto ERROR3;
|
||||
|
||||
/* Register a new directory entry with module sensors */
|
||||
if ((err = sensors_register_entry(new_client,type_name,
|
||||
lm78_dir_table_template)) < 0)
|
||||
goto ERROR4;
|
||||
((struct lm78_data *) (new_client->data))->sysctl_id = err;
|
||||
err = 0;
|
||||
|
||||
/* Initialize the LM78 chip */
|
||||
lm78_init_client(new_client);
|
||||
continue;
|
||||
|
||||
/* OK, this is not exactly good programming practice, usually. But it is
|
||||
very code-efficient in this case. */
|
||||
ERROR4:
|
||||
i2c_detach_client(new_client);
|
||||
ERROR3:
|
||||
lm78_remove_client((struct i2c_client *) new_client);
|
||||
ERROR2:
|
||||
kfree(new_client);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int lm78_detach_smbus(struct i2c_client *client)
|
||||
{
|
||||
int err,i;
|
||||
for (i = 0; i < MAX_LM78_NR; i++)
|
||||
if (client == lm78_list[i])
|
||||
break;
|
||||
if ((i == MAX_LM78_NR)) {
|
||||
printk("lm78.o: Client to detach not found.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
sensors_deregister_entry(((struct lm78_data *)(client->data))->sysctl_id);
|
||||
|
||||
if ((err = i2c_detach_client(client))) {
|
||||
printk("lm78.o: Client deregistration failed, client not detached.\n");
|
||||
return err;
|
||||
}
|
||||
lm78_remove_client(client);
|
||||
kfree(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Find a free slot, and initialize most of the fields */
|
||||
int lm78_new_client(struct i2c_adapter *adapter,
|
||||
struct i2c_client *new_client)
|
||||
{
|
||||
int i;
|
||||
struct lm78_data *data;
|
||||
|
||||
/* First, seek out an empty slot */
|
||||
for(i = 0; i < MAX_LM78_NR; i++)
|
||||
if (! lm78_list[i])
|
||||
break;
|
||||
if (i == MAX_LM78_NR) {
|
||||
printk("lm78.o: No empty slots left, recompile and heighten "
|
||||
"MAX_LM78_NR!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
lm78_list[i] = new_client;
|
||||
new_client->id = i;
|
||||
new_client->adapter = adapter;
|
||||
new_client->driver = &lm78_driver;
|
||||
data = new_client->data;
|
||||
data->valid = 0;
|
||||
data->lock = MUTEX;
|
||||
data->update_lock = MUTEX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Inverse of lm78_new_client */
|
||||
void lm78_remove_client(struct i2c_client *client)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAX_LM78_NR; i++)
|
||||
if (client == lm78_list[i])
|
||||
lm78_list[i] = NULL;
|
||||
}
|
||||
|
||||
/* No commands defined yet */
|
||||
int lm78_command(struct i2c_client *client, unsigned int cmd, void *arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Nothing here yet */
|
||||
void lm78_inc_use (struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_INC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Nothing here yet */
|
||||
void lm78_dec_use (struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_DEC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* The SMBus locks itself, but ISA access must be locked explicitely!
|
||||
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! */
|
||||
int lm78_read_value(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
int res;
|
||||
if (i2c_is_isa_client(client)) {
|
||||
down((struct semaphore *) (client->data));
|
||||
outb_p(reg,(((struct isa_client *) client)->isa_addr) +
|
||||
LM78_ADDR_REG_OFFSET);
|
||||
res = inb_p((((struct isa_client *) client)->isa_addr) +
|
||||
LM78_DATA_REG_OFFSET);
|
||||
up((struct semaphore *) (client->data));
|
||||
return res;
|
||||
} else
|
||||
return smbus_read_byte_data(client->adapter,client->addr, reg);
|
||||
}
|
||||
|
||||
/* The SMBus locks itself, but ISA access muse be locked explicitely!
|
||||
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! */
|
||||
int lm78_write_value(struct i2c_client *client, u8 reg, u8 value)
|
||||
{
|
||||
if (i2c_is_isa_client(client)) {
|
||||
down((struct semaphore *) (client->data));
|
||||
outb_p(reg,((struct isa_client *) client)->isa_addr + LM78_ADDR_REG_OFFSET);
|
||||
outb_p(value,((struct isa_client *) client)->isa_addr + LM78_DATA_REG_OFFSET);
|
||||
up((struct semaphore *) (client->data));
|
||||
return 0;
|
||||
} else
|
||||
return smbus_write_byte_data(client->adapter, client->addr, reg,value);
|
||||
}
|
||||
|
||||
/* Called when we have found a new LM78. It should set limits, etc. */
|
||||
void lm78_init_client(struct i2c_client *client)
|
||||
{
|
||||
int vid;
|
||||
|
||||
/* Reset all except Watchdog values and last conversion values
|
||||
This sets fan-divs to 2, among others */
|
||||
lm78_write_value(client,LM78_REG_CONFIG,0x80);
|
||||
|
||||
vid = lm78_read_value(client,LM78_REG_VID_FANDIV) & 0x0f;
|
||||
if (((struct lm78_data *) (client->data))->type == lm79)
|
||||
vid |= (lm78_read_value(client,LM78_REG_CHIPID) & 0x01) >> 4;
|
||||
else
|
||||
vid |= 0x10;
|
||||
vid = VID_FROM_REG(vid);
|
||||
|
||||
lm78_write_value(client,LM78_REG_IN_MIN(0),IN_TO_REG(LM78_INIT_IN_MIN_0));
|
||||
lm78_write_value(client,LM78_REG_IN_MAX(0),IN_TO_REG(LM78_INIT_IN_MAX_0));
|
||||
lm78_write_value(client,LM78_REG_IN_MIN(1),IN_TO_REG(LM78_INIT_IN_MIN_1));
|
||||
lm78_write_value(client,LM78_REG_IN_MAX(1),IN_TO_REG(LM78_INIT_IN_MAX_1));
|
||||
lm78_write_value(client,LM78_REG_IN_MIN(2),IN_TO_REG(LM78_INIT_IN_MIN_2));
|
||||
lm78_write_value(client,LM78_REG_IN_MAX(2),IN_TO_REG(LM78_INIT_IN_MAX_2));
|
||||
lm78_write_value(client,LM78_REG_IN_MIN(3),IN_TO_REG(LM78_INIT_IN_MIN_3));
|
||||
lm78_write_value(client,LM78_REG_IN_MAX(3),IN_TO_REG(LM78_INIT_IN_MAX_3));
|
||||
lm78_write_value(client,LM78_REG_IN_MIN(4),IN_TO_REG(LM78_INIT_IN_MIN_4));
|
||||
lm78_write_value(client,LM78_REG_IN_MAX(4),IN_TO_REG(LM78_INIT_IN_MAX_4));
|
||||
lm78_write_value(client,LM78_REG_IN_MIN(5),IN_TO_REG(LM78_INIT_IN_MIN_5));
|
||||
lm78_write_value(client,LM78_REG_IN_MAX(5),IN_TO_REG(LM78_INIT_IN_MAX_5));
|
||||
lm78_write_value(client,LM78_REG_IN_MIN(6),IN_TO_REG(LM78_INIT_IN_MIN_6));
|
||||
lm78_write_value(client,LM78_REG_IN_MAX(6),IN_TO_REG(LM78_INIT_IN_MAX_6));
|
||||
lm78_write_value(client,LM78_REG_FAN_MIN(1),
|
||||
FAN_TO_REG(LM78_INIT_FAN_MIN_1,2));
|
||||
lm78_write_value(client,LM78_REG_FAN_MIN(2),
|
||||
FAN_TO_REG(LM78_INIT_FAN_MIN_2,2));
|
||||
lm78_write_value(client,LM78_REG_FAN_MIN(3),
|
||||
FAN_TO_REG(LM78_INIT_FAN_MIN_3,2));
|
||||
lm78_write_value(client,LM78_REG_TEMP_OVER,TEMP_TO_REG(LM78_INIT_TEMP_OVER));
|
||||
lm78_write_value(client,LM78_REG_TEMP_HYST,TEMP_TO_REG(LM78_INIT_TEMP_HYST));
|
||||
|
||||
/* Start monitoring */
|
||||
lm78_write_value(client,LM78_REG_CONFIG,
|
||||
(lm78_read_value(client,LM78_REG_CONFIG) & 0xf7) | 0x01);
|
||||
|
||||
}
|
||||
|
||||
void lm78_update_client(struct i2c_client *client)
|
||||
{
|
||||
struct lm78_data *data = client->data;
|
||||
int i;
|
||||
|
||||
down(&data->update_lock);
|
||||
|
||||
if ((jiffies - data->last_updated > HZ+HZ/2 ) ||
|
||||
(jiffies < data->last_updated) || ! data->valid) {
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("Starting lm78 update\n");
|
||||
#endif
|
||||
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 = 1; i <= 3; i++) {
|
||||
data->fan[i-1] = lm78_read_value(client,LM78_REG_FAN(i));
|
||||
data->fan_min[i-1] = 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);
|
||||
}
|
||||
|
||||
|
||||
/* The next few functions are the call-back functions of the /proc/sys and
|
||||
sysctl files. Which function is used is defined in the ctl_table in
|
||||
the extra1 field.
|
||||
Each function must return the magnitude (power of 10 to divide the date
|
||||
with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
|
||||
put a maximum of *nrels elements in results reflecting the data of this
|
||||
file, and set *nrels to the number it actually put in it, if operation==
|
||||
SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
|
||||
results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
|
||||
Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
|
||||
large enough (by checking the incoming value of *nrels). This is not very
|
||||
good practice, but as long as you put less than about 5 values in results,
|
||||
you can assume it is large enough. */
|
||||
void lm78_in(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct lm78_data *data = client->data;
|
||||
int nr = ctl_name - LM78_SYSCTL_IN0;
|
||||
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 2;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
lm78_update_client(client);
|
||||
results[0] = IN_FROM_REG(data->in_min[nr]);
|
||||
results[1] = IN_FROM_REG(data->in_max[nr]);
|
||||
results[2] = IN_FROM_REG(data->in[nr]);
|
||||
*nrels_mag = 3;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
if (*nrels_mag >= 1) {
|
||||
data->in_min[nr] = IN_TO_REG(results[0]);
|
||||
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]);
|
||||
lm78_write_value(client,LM78_REG_IN_MAX(nr),data->in_max[nr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lm78_fan(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct lm78_data *data = client->data;
|
||||
int nr = ctl_name - LM78_SYSCTL_FAN1 + 1;
|
||||
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 0;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
lm78_update_client(client);
|
||||
results[0] = FAN_FROM_REG(data->fan_min[nr-1],
|
||||
DIV_FROM_REG(data->fan_div[nr-1]));
|
||||
results[1] = FAN_FROM_REG(data->fan[nr-1],
|
||||
DIV_FROM_REG(data->fan_div[nr-1]));
|
||||
*nrels_mag = 2;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
if (*nrels_mag >= 1) {
|
||||
data->fan_min[nr-1] = FAN_TO_REG(results[0],
|
||||
DIV_FROM_REG(data->fan_div[nr-1]));
|
||||
lm78_write_value(client,LM78_REG_FAN_MIN(nr),data->fan_min[nr-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void lm78_temp(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct lm78_data *data = client->data;
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 1;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
lm78_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]);
|
||||
lm78_write_value(client,LM78_REG_TEMP_OVER,data->temp_over);
|
||||
}
|
||||
if (*nrels_mag >= 2) {
|
||||
data->temp_hyst = TEMP_TO_REG(results[1]);
|
||||
lm78_write_value(client,LM78_REG_TEMP_HYST,data->temp_hyst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lm78_vid(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct lm78_data *data = client->data;
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 2;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
lm78_update_client(client);
|
||||
results[0] = VID_FROM_REG(data->vid);
|
||||
*nrels_mag = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void lm78_alarms(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct lm78_data *data = client->data;
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 0;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
lm78_update_client(client);
|
||||
results[0] = ALARMS_FROM_REG(data->alarms);
|
||||
*nrels_mag = 1;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
lm78_update_client(client);
|
||||
results[0] = DIV_FROM_REG(data->fan_div[0]);
|
||||
results[1] = DIV_FROM_REG(data->fan_div[1]);
|
||||
results[2] = 2;
|
||||
*nrels_mag = 3;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
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]);
|
||||
old = (old & 0xcf) | (data->fan_div[0] << 4);
|
||||
lm78_write_value(client,LM78_REG_VID_FANDIV,old);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int lm78_init(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
printk("lm78.o version %s (%s)\n",LM_VERSION,LM_DATE);
|
||||
lm78_initialized = 0;
|
||||
|
||||
if ((res =i2c_add_driver(&lm78_driver))) {
|
||||
printk("lm78.o: Driver registration failed, module not inserted.\n");
|
||||
lm78_cleanup();
|
||||
return res;
|
||||
}
|
||||
lm78_initialized ++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lm78_cleanup(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (lm78_initialized >= 1) {
|
||||
if ((res = i2c_del_driver(&lm78_driver))) {
|
||||
printk("lm78.o: Driver deregistration failed, module not removed.\n");
|
||||
return res;
|
||||
}
|
||||
lm78_initialized --;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MODULE
|
||||
|
||||
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
|
||||
MODULE_DESCRIPTION("LM78, LM78-J and LM79 driver");
|
||||
|
||||
int init_module(void)
|
||||
{
|
||||
return lm78_init();
|
||||
}
|
||||
|
||||
int cleanup_module(void)
|
||||
{
|
||||
return lm78_cleanup();
|
||||
}
|
||||
|
||||
#endif /* MODULE */
|
||||
|
||||
-689
@@ -1,689 +0,0 @@
|
||||
/*
|
||||
lm80.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
Copyright (c) 1998 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/malloc.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/types.h>
|
||||
#include "smbus.h"
|
||||
#include "version.h"
|
||||
#include "isa.h"
|
||||
#include "sensors.h"
|
||||
#include "i2c.h"
|
||||
#include "compat.h"
|
||||
|
||||
/* 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_MIN 0x3c
|
||||
#define LM80_REG_FAN2_MIN 0x3d
|
||||
#define LM80_REG_FAN1 0x28
|
||||
#define LM80_REG_FAN2 0x29
|
||||
|
||||
#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 is only done on the TO_REG variants. */
|
||||
#define IN_TO_REG(val,nr) ((val) & 0xff)
|
||||
#define IN_FROM_REG(val,nr) (val)
|
||||
|
||||
#define FAN_TO_REG(val,div) ((val)==0?255:\
|
||||
((1350000+(val)*(div)/2)/((val)*(div))) & 0xff)
|
||||
#define FAN_FROM_REG(val,div) ((val)==0?-1:\
|
||||
(val)==255?0:1350000/((div)*(val)))
|
||||
|
||||
#define TEMP_FROM_REG(val) lm80_temp_from_reg(val)
|
||||
|
||||
#define TEMP_LIMIT_TO_REG(val) (((val)<0?(((val)-50)/100)&0xff:\
|
||||
((val)+50)/100) & 0xff)
|
||||
#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*100)
|
||||
|
||||
#define ALARMS_FROM_REG(val) (val)
|
||||
|
||||
#define DIV_FROM_REG(val) (1 << (val))
|
||||
#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1)
|
||||
|
||||
/* Initial limits */
|
||||
#define LM80_INIT_IN_0 190
|
||||
#define LM80_INIT_IN_1 190
|
||||
#define LM80_INIT_IN_2 190
|
||||
#define LM80_INIT_IN_3 190
|
||||
#define LM80_INIT_IN_4 190
|
||||
#define LM80_INIT_IN_5 190
|
||||
#define LM80_INIT_IN_6 190
|
||||
|
||||
#define LM80_INIT_IN_PERCENTAGE 10
|
||||
|
||||
#define LM80_INIT_IN_MIN_0 \
|
||||
(LM80_INIT_IN_0 - LM80_INIT_IN_0 * LM80_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM80_INIT_IN_MAX_0 \
|
||||
(LM80_INIT_IN_0 + LM80_INIT_IN_0 * LM80_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM80_INIT_IN_MIN_1 \
|
||||
(LM80_INIT_IN_1 - LM80_INIT_IN_1 * LM80_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM80_INIT_IN_MAX_1 \
|
||||
(LM80_INIT_IN_1 + LM80_INIT_IN_1 * LM80_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM80_INIT_IN_MIN_2 \
|
||||
(LM80_INIT_IN_2 - LM80_INIT_IN_2 * LM80_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM80_INIT_IN_MAX_2 \
|
||||
(LM80_INIT_IN_2 + LM80_INIT_IN_2 * LM80_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM80_INIT_IN_MIN_3 \
|
||||
(LM80_INIT_IN_3 - LM80_INIT_IN_3 * LM80_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM80_INIT_IN_MAX_3 \
|
||||
(LM80_INIT_IN_3 + LM80_INIT_IN_3 * LM80_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM80_INIT_IN_MIN_4 \
|
||||
(LM80_INIT_IN_4 - LM80_INIT_IN_4 * LM80_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM80_INIT_IN_MAX_4 \
|
||||
(LM80_INIT_IN_4 + LM80_INIT_IN_4 * LM80_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM80_INIT_IN_MIN_5 \
|
||||
(LM80_INIT_IN_5 - LM80_INIT_IN_5 * LM80_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM80_INIT_IN_MAX_5 \
|
||||
(LM80_INIT_IN_5 + LM80_INIT_IN_5 * LM80_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM80_INIT_IN_MIN_6 \
|
||||
(LM80_INIT_IN_6 - LM80_INIT_IN_6 * LM80_INIT_IN_PERCENTAGE / 100)
|
||||
#define LM80_INIT_IN_MAX_6 \
|
||||
(LM80_INIT_IN_6 + LM80_INIT_IN_6 * LM80_INIT_IN_PERCENTAGE / 100)
|
||||
|
||||
#define LM80_INIT_FAN_MIN_1 3000
|
||||
#define LM80_INIT_FAN_MIN_2 3000
|
||||
|
||||
#define LM80_INIT_TEMP_OS_MAX 600
|
||||
#define LM80_INIT_TEMP_OS_HYST 500
|
||||
#define LM80_INIT_TEMP_HOT_MAX 700
|
||||
#define LM80_INIT_TEMP_HOT_HYST 600
|
||||
|
||||
#ifdef MODULE
|
||||
extern int init_module(void);
|
||||
extern int cleanup_module(void);
|
||||
#endif /* MODULE */
|
||||
|
||||
/* For each registered LM80, we need to keep some data in memory. That
|
||||
data is pointed to by lm80_list[NR]->data. The structure itself is
|
||||
dynamically allocated, at the same time when a new lm80 client is
|
||||
allocated. */
|
||||
struct lm80_data {
|
||||
int sysctl_id;
|
||||
|
||||
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 */
|
||||
};
|
||||
|
||||
|
||||
static int lm80_init(void);
|
||||
static int lm80_cleanup(void);
|
||||
|
||||
static int lm80_attach_adapter(struct i2c_adapter *adapter);
|
||||
static int lm80_detach_client(struct i2c_client *client);
|
||||
static int lm80_new_client(struct i2c_adapter *adapter,
|
||||
struct i2c_client *new_client);
|
||||
static void lm80_remove_client(struct i2c_client *client);
|
||||
static int lm80_command(struct i2c_client *client, unsigned int cmd,
|
||||
void *arg);
|
||||
static void lm80_inc_use (struct i2c_client *client);
|
||||
static void lm80_dec_use (struct i2c_client *client);
|
||||
|
||||
static long lm80_temp_from_reg(u16 regs);
|
||||
|
||||
static int lm80_read_value(struct i2c_client *client, u8 register);
|
||||
static int lm80_write_value(struct i2c_client *client, u8 register, u8 value);
|
||||
static void lm80_update_client(struct i2c_client *client);
|
||||
static void lm80_init_client(struct i2c_client *client);
|
||||
|
||||
|
||||
static void lm80_in(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void lm80_fan(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void lm80_temp(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void lm80_alarms(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void lm80_fan_div(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
|
||||
/* I choose here for semi-static LM80 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_LM80_NR 4
|
||||
static struct i2c_client *lm80_list[MAX_LM80_NR];
|
||||
|
||||
/* 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 lm80_driver = {
|
||||
/* name */ "LM80 sensor driver",
|
||||
/* id */ I2C_DRIVERID_LM80,
|
||||
/* flags */ DF_NOTIFY,
|
||||
/* attach_adapter */ &lm80_attach_adapter,
|
||||
/* detach_client */ &lm80_detach_client,
|
||||
/* command */ &lm80_command,
|
||||
/* inc_use */ &lm80_inc_use,
|
||||
/* dec_use */ &lm80_dec_use
|
||||
};
|
||||
|
||||
/* Used by lm80_init/cleanup */
|
||||
static int lm80_initialized = 0;
|
||||
|
||||
/* The /proc/sys entries */
|
||||
/* These files are created for each detected LM80. 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 lm80_dir_table_template[] = {
|
||||
{ LM80_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm80_in },
|
||||
{ LM80_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm80_in },
|
||||
{ LM80_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm80_in },
|
||||
{ LM80_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm80_in },
|
||||
{ LM80_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm80_in },
|
||||
{ LM80_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm80_in },
|
||||
{ LM80_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm80_in },
|
||||
{ LM80_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm80_fan },
|
||||
{ LM80_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm80_fan },
|
||||
{ LM80_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm80_temp },
|
||||
{ LM80_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm80_fan_div },
|
||||
{ LM80_SYSCTL_ALARMS, "alarms", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, &lm80_alarms },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
||||
int lm80_attach_adapter(struct i2c_adapter *adapter)
|
||||
{
|
||||
int address,err;
|
||||
struct i2c_client *new_client;
|
||||
const char *type_name,*client_name;
|
||||
|
||||
/* OK, this is no detection. I know. It will do for now, though. */
|
||||
err = 0;
|
||||
for (address = 0x20; (! err) && (address <= 0x2f); address ++) {
|
||||
|
||||
/* Later on, we will keep a list of registered addresses for each
|
||||
adapter, and check whether they are used here */
|
||||
|
||||
if (smbus_read_byte_data(adapter,address,LM80_REG_CONFIG) < 0)
|
||||
continue;
|
||||
|
||||
/* Real detection code goes here */
|
||||
|
||||
printk("lm80.o: LM80 detected\n");
|
||||
type_name = "lm80";
|
||||
client_name = "LM80 chip";
|
||||
|
||||
|
||||
/* Allocate space for a new client structure. To counter memory
|
||||
fragmentation somewhat, we only do one kmalloc. */
|
||||
if (! (new_client = kmalloc(sizeof(struct i2c_client) +
|
||||
sizeof(struct lm80_data),
|
||||
GFP_KERNEL))) {
|
||||
err = -ENOMEM;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Fill the new client structure with data */
|
||||
new_client->data = (struct lm80_data *) (new_client + 1);
|
||||
new_client->addr = address;
|
||||
strcpy(new_client->name,client_name);
|
||||
if ((err = lm80_new_client(adapter,new_client)))
|
||||
goto ERROR2;
|
||||
|
||||
/* Tell i2c-core a new client has arrived */
|
||||
if ((err = i2c_attach_client(new_client)))
|
||||
goto ERROR3;
|
||||
|
||||
/* Register a new directory entry with module sensors */
|
||||
if ((err = sensors_register_entry(new_client,type_name,
|
||||
lm80_dir_table_template)) < 0)
|
||||
goto ERROR4;
|
||||
((struct lm80_data *) (new_client->data))->sysctl_id = err;
|
||||
err = 0;
|
||||
|
||||
/* Initialize the LM80 chip */
|
||||
lm80_init_client(new_client);
|
||||
continue;
|
||||
|
||||
/* OK, this is not exactly good programming practice, usually. But it is
|
||||
very code-efficient in this case. */
|
||||
ERROR4:
|
||||
i2c_detach_client(new_client);
|
||||
ERROR3:
|
||||
lm80_remove_client((struct i2c_client *) new_client);
|
||||
ERROR2:
|
||||
kfree(new_client);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int lm80_detach_client(struct i2c_client *client)
|
||||
{
|
||||
int err,i;
|
||||
for (i = 0; i < MAX_LM80_NR; i++)
|
||||
if (client == lm80_list[i])
|
||||
break;
|
||||
if ((i == MAX_LM80_NR)) {
|
||||
printk("lm80.o: Client to detach not found.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
sensors_deregister_entry(((struct lm80_data *)(client->data))->sysctl_id);
|
||||
|
||||
if ((err = i2c_detach_client(client))) {
|
||||
printk("lm80.o: Client deregistration failed, client not detached.\n");
|
||||
return err;
|
||||
}
|
||||
lm80_remove_client(client);
|
||||
kfree(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Find a free slot, and initialize most of the fields */
|
||||
int lm80_new_client(struct i2c_adapter *adapter,
|
||||
struct i2c_client *new_client)
|
||||
{
|
||||
int i;
|
||||
struct lm80_data *data;
|
||||
|
||||
/* First, seek out an empty slot */
|
||||
for(i = 0; i < MAX_LM80_NR; i++)
|
||||
if (! lm80_list[i])
|
||||
break;
|
||||
if (i == MAX_LM80_NR) {
|
||||
printk("lm80.o: No empty slots left, recompile and heighten "
|
||||
"MAX_LM80_NR!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
lm80_list[i] = new_client;
|
||||
new_client->id = i;
|
||||
new_client->adapter = adapter;
|
||||
new_client->driver = &lm80_driver;
|
||||
data = new_client->data;
|
||||
data->valid = 0;
|
||||
data->update_lock = MUTEX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Inverse of lm80_new_client */
|
||||
void lm80_remove_client(struct i2c_client *client)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAX_LM80_NR; i++)
|
||||
if (client == lm80_list[i])
|
||||
lm80_list[i] = NULL;
|
||||
}
|
||||
|
||||
/* No commands defined yet */
|
||||
int lm80_command(struct i2c_client *client, unsigned int cmd, void *arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lm80_inc_use (struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_INC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
void lm80_dec_use (struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_DEC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
long lm80_temp_from_reg(u16 temp)
|
||||
{
|
||||
long res;
|
||||
res = ((temp & 0xff00) >> 8) * 10000 + ((temp & 0x10) >> 4) * 5000 +
|
||||
((temp & 0x20) >> 5) * 2500 + ((temp & 0x40) >> 6) * 1250 +
|
||||
((temp & 0x80) >> 7) * 625;
|
||||
temp /= 100;
|
||||
if (temp >= 0x80 * 100)
|
||||
temp = - (0x100 * 100 - temp);
|
||||
return temp;
|
||||
}
|
||||
|
||||
int lm80_read_value(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
return smbus_read_byte_data(client->adapter,client->addr, reg);
|
||||
}
|
||||
|
||||
int lm80_write_value(struct i2c_client *client, u8 reg, u8 value)
|
||||
{
|
||||
return smbus_write_byte_data(client->adapter, client->addr, reg,value);
|
||||
}
|
||||
|
||||
/* Called when we have found a new LM80. It should set limits, etc. */
|
||||
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);
|
||||
|
||||
lm80_write_value(client,LM80_REG_IN_MIN(0),IN_TO_REG(LM80_INIT_IN_MIN_0,0));
|
||||
lm80_write_value(client,LM80_REG_IN_MAX(0),IN_TO_REG(LM80_INIT_IN_MAX_0,0));
|
||||
lm80_write_value(client,LM80_REG_IN_MIN(1),IN_TO_REG(LM80_INIT_IN_MIN_1,1));
|
||||
lm80_write_value(client,LM80_REG_IN_MAX(1),IN_TO_REG(LM80_INIT_IN_MAX_1,1));
|
||||
lm80_write_value(client,LM80_REG_IN_MIN(2),IN_TO_REG(LM80_INIT_IN_MIN_2,2));
|
||||
lm80_write_value(client,LM80_REG_IN_MAX(2),IN_TO_REG(LM80_INIT_IN_MAX_2,2));
|
||||
lm80_write_value(client,LM80_REG_IN_MIN(3),IN_TO_REG(LM80_INIT_IN_MIN_3,3));
|
||||
lm80_write_value(client,LM80_REG_IN_MAX(3),IN_TO_REG(LM80_INIT_IN_MAX_3,3));
|
||||
lm80_write_value(client,LM80_REG_IN_MIN(4),IN_TO_REG(LM80_INIT_IN_MIN_4,4));
|
||||
lm80_write_value(client,LM80_REG_IN_MAX(4),IN_TO_REG(LM80_INIT_IN_MAX_4,4));
|
||||
lm80_write_value(client,LM80_REG_IN_MIN(5),IN_TO_REG(LM80_INIT_IN_MIN_5,5));
|
||||
lm80_write_value(client,LM80_REG_IN_MAX(5),IN_TO_REG(LM80_INIT_IN_MAX_5,5));
|
||||
lm80_write_value(client,LM80_REG_IN_MIN(6),IN_TO_REG(LM80_INIT_IN_MIN_6,6));
|
||||
lm80_write_value(client,LM80_REG_IN_MAX(6),IN_TO_REG(LM80_INIT_IN_MAX_6,6));
|
||||
lm80_write_value(client,LM80_REG_FAN1_MIN,FAN_TO_REG(LM80_INIT_FAN_MIN_1,2));
|
||||
lm80_write_value(client,LM80_REG_FAN2_MIN,FAN_TO_REG(LM80_INIT_FAN_MIN_2,2));
|
||||
lm80_write_value(client,LM80_REG_TEMP_HOT_MAX,
|
||||
TEMP_LIMIT_TO_REG(LM80_INIT_TEMP_OS_MAX));
|
||||
lm80_write_value(client,LM80_REG_TEMP_HOT_HYST,
|
||||
TEMP_LIMIT_TO_REG(LM80_INIT_TEMP_OS_HYST));
|
||||
lm80_write_value(client,LM80_REG_TEMP_OS_MAX,
|
||||
TEMP_LIMIT_TO_REG(LM80_INIT_TEMP_OS_MAX));
|
||||
lm80_write_value(client,LM80_REG_TEMP_OS_HYST,
|
||||
TEMP_LIMIT_TO_REG(LM80_INIT_TEMP_OS_HYST));
|
||||
|
||||
/* Start monitoring */
|
||||
lm80_write_value(client,LM80_REG_CONFIG,0x01);
|
||||
}
|
||||
|
||||
void lm80_update_client(struct i2c_client *client)
|
||||
{
|
||||
struct lm80_data *data = client->data;
|
||||
int i;
|
||||
|
||||
down(&data->update_lock);
|
||||
|
||||
if ((jiffies - data->last_updated > 2*HZ ) ||
|
||||
(jiffies < data->last_updated) || ! data->valid) {
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("Starting lm80 update\n");
|
||||
#endif
|
||||
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_FAN1_MIN);
|
||||
data->fan[1] = lm80_read_value(client,LM80_REG_FAN2);
|
||||
data->fan_min[1] = lm80_read_value(client,LM80_REG_FAN2_MIN);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/* The next few functions are the call-back functions of the /proc/sys and
|
||||
sysctl files. Which function is used is defined in the ctl_table in
|
||||
the extra1 field.
|
||||
Each function must return the magnitude (power of 10 to divide the date
|
||||
with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
|
||||
put a maximum of *nrels elements in results reflecting the data of this
|
||||
file, and set *nrels to the number it actually put in it, if operation==
|
||||
SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
|
||||
results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
|
||||
Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
|
||||
large enough (by checking the incoming value of *nrels). This is not very
|
||||
good practice, but as long as you put less than about 5 values in results,
|
||||
you can assume it is large enough. */
|
||||
void lm80_in(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct lm80_data *data = client->data;
|
||||
int nr = ctl_name - LM80_SYSCTL_IN0;
|
||||
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 2;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
lm80_update_client(client);
|
||||
results[0] = IN_FROM_REG(data->in_min[nr],nr);
|
||||
results[1] = IN_FROM_REG(data->in_max[nr],nr);
|
||||
results[2] = IN_FROM_REG(data->in[nr],nr);
|
||||
*nrels_mag = 3;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
if (*nrels_mag >= 1) {
|
||||
data->in_min[nr] = IN_TO_REG(results[0],nr);
|
||||
lm80_write_value(client,LM80_REG_IN_MIN(nr),data->in_min[nr]);
|
||||
}
|
||||
if (*nrels_mag >= 2) {
|
||||
data->in_max[nr] = IN_TO_REG(results[1],nr);
|
||||
lm80_write_value(client,LM80_REG_IN_MAX(nr),data->in_max[nr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lm80_fan(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct lm80_data *data = client->data;
|
||||
int nr = ctl_name - LM80_SYSCTL_FAN1 + 1;
|
||||
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 0;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
lm80_update_client(client);
|
||||
results[0] = FAN_FROM_REG(data->fan_min[nr-1],
|
||||
DIV_FROM_REG(data->fan_div[nr-1]));
|
||||
results[1] = FAN_FROM_REG(data->fan[nr-1],
|
||||
DIV_FROM_REG(data->fan_div[nr-1]));
|
||||
*nrels_mag = 2;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
if (*nrels_mag >= 1) {
|
||||
data->fan_min[nr-1] = FAN_TO_REG(results[0],
|
||||
DIV_FROM_REG(data->fan_div[nr-1]));
|
||||
lm80_write_value(client,nr==1?LM80_REG_FAN1_MIN:LM80_REG_FAN2_MIN,
|
||||
data->fan_min[nr-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void lm80_temp(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct lm80_data *data = client->data;
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 1;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
lm80_update_client(client);
|
||||
results[0] = TEMP_LIMIT_FROM_REG(data->temp_hot_max);
|
||||
results[1] = TEMP_LIMIT_FROM_REG(data->temp_hot_hyst);
|
||||
results[2] = TEMP_LIMIT_FROM_REG(data->temp_os_max);
|
||||
results[3] = TEMP_LIMIT_FROM_REG(data->temp_os_hyst);
|
||||
results[4] = TEMP_FROM_REG(data->temp);
|
||||
*nrels_mag = 5;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
if (*nrels_mag >= 1) {
|
||||
data->temp_hot_max = TEMP_LIMIT_TO_REG(results[0]);
|
||||
lm80_write_value(client,LM80_REG_TEMP_HOT_MAX,data->temp_hot_max);
|
||||
}
|
||||
if (*nrels_mag >= 2) {
|
||||
data->temp_hot_hyst = TEMP_LIMIT_TO_REG(results[1]);
|
||||
lm80_write_value(client,LM80_REG_TEMP_HOT_HYST,data->temp_hot_hyst);
|
||||
}
|
||||
if (*nrels_mag >= 3) {
|
||||
data->temp_os_max = TEMP_LIMIT_TO_REG(results[2]);
|
||||
lm80_write_value(client,LM80_REG_TEMP_OS_MAX,data->temp_os_max);
|
||||
}
|
||||
if (*nrels_mag >= 4) {
|
||||
data->temp_os_hyst = TEMP_LIMIT_TO_REG(results[3]);
|
||||
lm80_write_value(client,LM80_REG_TEMP_OS_HYST,data->temp_os_hyst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lm80_alarms(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct lm80_data *data = client->data;
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 0;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
lm80_update_client(client);
|
||||
results[0] = ALARMS_FROM_REG(data->alarms);
|
||||
*nrels_mag = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void lm80_fan_div(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct lm80_data *data = client->data;
|
||||
int old;
|
||||
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 0;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
lm80_update_client(client);
|
||||
results[0] = DIV_FROM_REG(data->fan_div[0]);
|
||||
results[1] = DIV_FROM_REG(data->fan_div[1]);
|
||||
results[2] = 2;
|
||||
*nrels_mag = 3;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
old = lm80_read_value(client,LM80_REG_FANDIV);
|
||||
if (*nrels_mag >= 2) {
|
||||
data->fan_div[1] = DIV_TO_REG(results[1]);
|
||||
old = (old & 0xcf) | (data->fan_div[1] << 4);
|
||||
}
|
||||
if (*nrels_mag >= 1) {
|
||||
data->fan_div[0] = DIV_TO_REG(results[0]);
|
||||
old = (old & 0xf3) | (data->fan_div[0] << 2);
|
||||
lm80_write_value(client,LM80_REG_FANDIV,old);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int lm80_init(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
printk("lm80.o version %s (%s)\n",LM_VERSION,LM_DATE);
|
||||
lm80_initialized = 0;
|
||||
|
||||
if ((res =i2c_add_driver(&lm80_driver))) {
|
||||
printk("lm80.o: Driver registration failed, module not inserted.\n");
|
||||
lm80_cleanup();
|
||||
return res;
|
||||
}
|
||||
lm80_initialized ++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lm80_cleanup(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (lm80_initialized >= 1) {
|
||||
if ((res = i2c_del_driver(&lm80_driver))) {
|
||||
printk("lm80.o: Driver deregistration failed, module not removed.\n");
|
||||
return res;
|
||||
}
|
||||
lm80_initialized --;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MODULE
|
||||
|
||||
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
|
||||
MODULE_DESCRIPTION("LM80 driver");
|
||||
|
||||
int init_module(void)
|
||||
{
|
||||
return lm80_init();
|
||||
}
|
||||
|
||||
int cleanup_module(void)
|
||||
{
|
||||
return lm80_cleanup();
|
||||
}
|
||||
|
||||
#endif /* MODULE */
|
||||
|
||||
-357
@@ -1,357 +0,0 @@
|
||||
/*
|
||||
ltc1710.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
Copyright (c) 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.
|
||||
*/
|
||||
|
||||
/* A few notes about the LTC1710:
|
||||
|
||||
* The LTC1710 is a dual programmable switch. It can be used to turn
|
||||
anything on or off anything which consumes less than 300mA of
|
||||
current and up to 5.5V
|
||||
|
||||
* The LTC1710 is a very, very simple SMBus device with three possible
|
||||
SMBus addresses (0x58,0x59, or 0x5A). Only SMBus byte writes
|
||||
(command writes) are supported.
|
||||
|
||||
* Since only writes are supported, READS DON'T WORK! The device
|
||||
plays dead in the event of a read, so this makes detection a
|
||||
bit tricky.
|
||||
|
||||
* BTW- I can safely say that this driver has been tested under
|
||||
every possible case, so there should be no bugs. :')
|
||||
|
||||
--Phil
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/malloc.h>
|
||||
#include "smbus.h"
|
||||
#include "sensors.h"
|
||||
#include "i2c.h"
|
||||
#include "version.h"
|
||||
|
||||
|
||||
/* The LTC1710 registers */
|
||||
|
||||
/* (No registers. [Wow! This thing is SIMPLE!] ) */
|
||||
|
||||
/* Initial values */
|
||||
#define LTC1710_INIT 0 /* Both off */
|
||||
|
||||
/* Each client has this additional data */
|
||||
struct ltc1710_data {
|
||||
int sysctl_id;
|
||||
|
||||
struct semaphore update_lock;
|
||||
char valid; /* !=0 if following fields are valid */
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
|
||||
u8 status; /* Register values */
|
||||
};
|
||||
|
||||
#ifdef MODULE
|
||||
extern int init_module(void);
|
||||
extern int cleanup_module(void);
|
||||
#endif /* MODULE */
|
||||
|
||||
static int ltc1710_init(void);
|
||||
static int ltc1710_cleanup(void);
|
||||
static int ltc1710_attach_adapter(struct i2c_adapter *adapter);
|
||||
static int ltc1710_detach_client(struct i2c_client *client);
|
||||
static int ltc1710_command(struct i2c_client *client, unsigned int cmd,
|
||||
void *arg);
|
||||
static void ltc1710_inc_use (struct i2c_client *client);
|
||||
static void ltc1710_dec_use (struct i2c_client *client);
|
||||
static u16 swap_bytes(u16 val);
|
||||
static void ltc1710_switch1(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void ltc1710_switch2(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
static void ltc1710_update_client(struct i2c_client *client);
|
||||
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver ltc1710_driver = {
|
||||
/* name */ "LTC1710 sensor chip driver",
|
||||
/* id */ I2C_DRIVERID_LTC1710,
|
||||
/* flags */ DF_NOTIFY,
|
||||
/* attach_adapter */ <c1710_attach_adapter,
|
||||
/* detach_client */ <c1710_detach_client,
|
||||
/* command */ <c1710_command,
|
||||
/* inc_use */ <c1710_inc_use,
|
||||
/* dec_use */ <c1710_dec_use
|
||||
};
|
||||
|
||||
/* These files are created for each detected LTC1710. 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 ltc1710_dir_table_template[] = {
|
||||
{ LTC1710_SYSCTL_SWITCH_1, "switch1", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, <c1710_switch1 },
|
||||
{ LTC1710_SYSCTL_SWITCH_2, "switch2", NULL, 0, 0644, NULL, &sensors_proc_real,
|
||||
&sensors_sysctl_real, NULL, <c1710_switch2 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
/* Used by init/cleanup */
|
||||
static int ltc1710_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_LTC1710_NR 3
|
||||
static struct i2c_client *ltc1710_list[MAX_LTC1710_NR];
|
||||
|
||||
|
||||
int ltc1710_attach_adapter(struct i2c_adapter *adapter)
|
||||
{
|
||||
int address,err,i;
|
||||
struct i2c_client *new_client;
|
||||
struct ltc1710_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 = 0x58; (! err) && (address <= 0x5A); address ++) {
|
||||
|
||||
/* Later on, we will keep a list of registered addresses for each
|
||||
adapter, and check whether they are used here */
|
||||
|
||||
/* We must do a write to test because a read fails! */
|
||||
if (smbus_write_byte(adapter,address,0) < 0)
|
||||
continue;
|
||||
|
||||
/* There is no real detection possible, the device is too simple. */
|
||||
|
||||
/* Allocate space for a new client structure */
|
||||
if (! (new_client = kmalloc(sizeof(struct i2c_client) +
|
||||
sizeof(struct ltc1710_data),
|
||||
GFP_KERNEL))) {
|
||||
err = -ENOMEM;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Find a place in our global list */
|
||||
for (i = 0; i < MAX_LTC1710_NR; i++)
|
||||
if (! ltc1710_list[i])
|
||||
break;
|
||||
if (i == MAX_LTC1710_NR) {
|
||||
err = -ENOMEM;
|
||||
printk("ltc1710.o: No empty slots left, recompile and heighten "
|
||||
"MAX_LTC1710_NR!\n");
|
||||
goto ERROR1;
|
||||
}
|
||||
ltc1710_list[i] = new_client;
|
||||
|
||||
/* Fill the new client structure with data */
|
||||
data = (struct ltc1710_data *) (new_client + 1);
|
||||
new_client->data = data;
|
||||
new_client->id = i;
|
||||
new_client->addr = address;
|
||||
new_client->adapter = adapter;
|
||||
new_client->driver = <c1710_driver;
|
||||
strcpy(new_client->name,"LTC1710 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,"ltc1710",
|
||||
ltc1710_dir_table_template)) < 0)
|
||||
goto ERROR3;
|
||||
data->sysctl_id = err;
|
||||
err = 0;
|
||||
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:
|
||||
ltc1710_list[i] = NULL;
|
||||
ERROR1:
|
||||
kfree(new_client);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int ltc1710_detach_client(struct i2c_client *client)
|
||||
{
|
||||
int err,i;
|
||||
for (i = 0; i < MAX_LTC1710_NR; i++)
|
||||
if (client == ltc1710_list[i])
|
||||
break;
|
||||
if ((i == MAX_LTC1710_NR)) {
|
||||
printk("ltc1710.o: Client to detach not found.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
sensors_deregister_entry(((struct ltc1710_data *)(client->data))->sysctl_id);
|
||||
|
||||
if ((err = i2c_detach_client(client))) {
|
||||
printk("ltc1710.o: Client deregistration failed, client not detached.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
ltc1710_list[i] = NULL;
|
||||
kfree(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* No commands defined yet */
|
||||
int ltc1710_command(struct i2c_client *client, unsigned int cmd, void *arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Nothing here yet */
|
||||
void ltc1710_inc_use (struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_INC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Nothing here yet */
|
||||
void ltc1710_dec_use (struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_DEC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void ltc1710_update_client(struct i2c_client *client)
|
||||
{
|
||||
struct ltc1710_data *data = client->data;
|
||||
|
||||
down(&data->update_lock);
|
||||
|
||||
if ((jiffies - data->last_updated > HZ+HZ/2 ) ||
|
||||
(jiffies < data->last_updated) || ! data->valid) {
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("Starting ltc1710 update\n");
|
||||
#endif
|
||||
|
||||
/* data->status = smbus_read_byte(client->adapter,client->addr);
|
||||
Unfortunately, reads always fail! */
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
up(&data->update_lock);
|
||||
}
|
||||
|
||||
|
||||
void ltc1710_switch1(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct ltc1710_data *data = client->data;
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 0;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
ltc1710_update_client(client);
|
||||
results[0] = data->status & 1;
|
||||
*nrels_mag = 1;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
if (*nrels_mag >= 1) {
|
||||
data->status = (data->status & 2) | results[0];
|
||||
smbus_write_byte(client->adapter,client->addr,data->status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ltc1710_switch2(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
{
|
||||
struct ltc1710_data *data = client->data;
|
||||
if (operation == SENSORS_PROC_REAL_INFO)
|
||||
*nrels_mag = 0;
|
||||
else if (operation == SENSORS_PROC_REAL_READ) {
|
||||
ltc1710_update_client(client);
|
||||
results[0] = (data->status & 2) >> 1;
|
||||
*nrels_mag = 1;
|
||||
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
||||
if (*nrels_mag >= 1) {
|
||||
data->status = (data->status & 1) | (results[0] << 1);
|
||||
smbus_write_byte(client->adapter,client->addr,data->status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ltc1710_init(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
printk("ltc1710.o version %s (%s)\n",LM_VERSION,LM_DATE);
|
||||
ltc1710_initialized = 0;
|
||||
if ((res = i2c_add_driver(<c1710_driver))) {
|
||||
printk("ltc1710.o: Driver registration failed, module not inserted.\n");
|
||||
ltc1710_cleanup();
|
||||
return res;
|
||||
}
|
||||
ltc1710_initialized ++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ltc1710_cleanup(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (ltc1710_initialized >= 1) {
|
||||
if ((res = i2c_del_driver(<c1710_driver))) {
|
||||
printk("ltc1710.o: Driver deregistration failed, module not removed.\n");
|
||||
return res;
|
||||
}
|
||||
ltc1710_initialized --;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MODULE
|
||||
|
||||
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
|
||||
MODULE_DESCRIPTION("LTC1710 driver");
|
||||
|
||||
int init_module(void)
|
||||
{
|
||||
return ltc1710_init();
|
||||
}
|
||||
|
||||
int cleanup_module(void)
|
||||
{
|
||||
return ltc1710_cleanup();
|
||||
}
|
||||
|
||||
#endif /* MODULE */
|
||||
|
||||
-449
@@ -1,449 +0,0 @@
|
||||
/*
|
||||
piix4.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
Copyright (c) 1998 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.
|
||||
*/
|
||||
|
||||
/* Note: we assume there can only be one PIIX4, with one SMBus interface */
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/ioport.h>
|
||||
#include "smbus.h"
|
||||
#include "version.h"
|
||||
#include "compat.h"
|
||||
|
||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,54))
|
||||
#include <linux/bios32.h>
|
||||
#endif
|
||||
|
||||
|
||||
/* PIIX4 SMBus address offsets */
|
||||
#define SMBHSTSTS (0 + piix4_smba)
|
||||
#define SMBHSLVSTS (1 + piix4_smba)
|
||||
#define SMBHSTCNT (2 + piix4_smba)
|
||||
#define SMBHSTCMD (3 + piix4_smba)
|
||||
#define SMBHSTADD (4 + piix4_smba)
|
||||
#define SMBHSTDAT0 (5 + piix4_smba)
|
||||
#define SMBHSTDAT1 (6 + piix4_smba)
|
||||
#define SMBBLKDAT (7 + piix4_smba)
|
||||
#define SMBSLVCNT (8 + piix4_smba)
|
||||
#define SMBSHDWCMD (9 + piix4_smba)
|
||||
#define SMBSLVEVT (0xA + piix4_smba)
|
||||
#define SMBSLVDAT (0xC + piix4_smba)
|
||||
|
||||
/* PCI Address Constants */
|
||||
#define SMBBA 0x090
|
||||
#define SMBHSTCFG 0x0D2
|
||||
#define SMBSLVC 0x0D3
|
||||
#define SMBSHDW1 0x0D4
|
||||
#define SMBSHDW2 0x0D5
|
||||
#define SMBREV 0x0D6
|
||||
|
||||
/* Other settings */
|
||||
#define MAX_TIMEOUT 500
|
||||
#define ENABLE_INT9 0
|
||||
|
||||
/* PIIX4 constants */
|
||||
#define PIIX4_QUICK 0x00
|
||||
#define PIIX4_BYTE 0x04
|
||||
#define PIIX4_BYTE_DATA 0x08
|
||||
#define PIIX4_WORD_DATA 0x0C
|
||||
#define PIIX4_BLOCK_DATA 0x14
|
||||
|
||||
|
||||
static int piix4_init(void);
|
||||
static int piix4_cleanup(void);
|
||||
static int piix4_setup(void);
|
||||
static s32 piix4_access(u8 addr, char read_write,
|
||||
u8 command, int size, union smbus_data * data);
|
||||
static void piix4_do_pause( unsigned int amount );
|
||||
static int piix4_transaction(void);
|
||||
|
||||
#ifdef MODULE
|
||||
extern int init_module(void);
|
||||
extern int cleanup_module(void);
|
||||
#endif /* MODULE */
|
||||
|
||||
static struct smbus_adapter piix4_adapter;
|
||||
static int piix4_initialized;
|
||||
static unsigned short piix4_smba = 0;
|
||||
|
||||
|
||||
/* Detect whether a PIIX4 can be found, and initialize it, where necessary.
|
||||
Note the differences between kernels with the old PCI BIOS interface and
|
||||
newer kernels with the real PCI interface. In compat.h some things are
|
||||
defined to make the transition easier. */
|
||||
int piix4_setup(void)
|
||||
{
|
||||
int error_return=0;
|
||||
unsigned char temp;
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,54))
|
||||
struct pci_dev *PIIX4_dev;
|
||||
#else
|
||||
unsigned char PIIX4_bus, PIIX4_devfn;
|
||||
int i,res;
|
||||
#endif
|
||||
|
||||
/* First check whether we can access PCI at all */
|
||||
if (pci_present() == 0) {
|
||||
printk("piix4.o: Error: No PCI-bus found!\n");
|
||||
error_return=-ENODEV;
|
||||
goto END;
|
||||
}
|
||||
|
||||
/* Look for the PIIX4, function 3 */
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,54))
|
||||
/* Note: we keep on searching until we have found 'function 3' */
|
||||
PIIX4_dev = NULL;
|
||||
do
|
||||
PIIX4_dev = pci_find_device(PCI_VENDOR_ID_INTEL,
|
||||
PCI_DEVICE_ID_INTEL_82371AB_3, PIIX4_dev);
|
||||
while(PIIX4_dev && (PCI_FUNC(PIIX4_dev->devfn) != 3));
|
||||
if(PIIX4_dev == NULL) {
|
||||
#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,1,54) */
|
||||
for (i = 0;
|
||||
! (res = pcibios_find_device(PCI_VENDOR_ID_INTEL,
|
||||
PCI_DEVICE_ID_INTEL_82371AB_3,
|
||||
i,&PIIX4_bus, &PIIX4_devfn)) &&
|
||||
PCI_FUNC(PIIX4_devfn) != 3;
|
||||
i++);
|
||||
|
||||
if (res) {
|
||||
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,54) */
|
||||
printk("piix4.o: Error: Can't detect PIIX4, function 3!\n");
|
||||
error_return=-ENODEV;
|
||||
goto END;
|
||||
}
|
||||
|
||||
/* Determine the address of the SMBus areas */
|
||||
pci_read_config_word_united(PIIX4_dev, PIIX4_bus ,PIIX4_devfn,
|
||||
SMBBA,&piix4_smba);
|
||||
piix4_smba &= 0xfff0;
|
||||
|
||||
if (check_region(piix4_smba, 8)) {
|
||||
printk("piix4.o: PIIX4_smb region 0x%x already in use!\n", piix4_smba);
|
||||
error_return=-ENODEV;
|
||||
goto END;
|
||||
}
|
||||
|
||||
pci_read_config_byte_united(PIIX4_dev, PIIX4_bus, PIIX4_devfn,
|
||||
SMBHSTCFG, &temp);
|
||||
|
||||
|
||||
#ifdef FORCE_PIIX4_ENABLE
|
||||
/* This should never need to be done, but has been noted that
|
||||
many Dell machines have the SMBus interface on the PIIX4
|
||||
disabled!? NOTE: This assumes I/O space and other allocations WERE
|
||||
done by the Bios! Don't complain if your hardware does weird
|
||||
things after enabling this. :') Check for Bios updates before
|
||||
resorting to this. */
|
||||
if ((temp & 1) == 0) {
|
||||
pci_write_config_byte_united(PIIX4_dev, PIIX4_bus, PIIX4_devfn,
|
||||
SMBHSTCFG, temp | 1);
|
||||
printk("piix4.0: WARNING: PIIX4 SMBus interface has been FORCEFULLY "
|
||||
"ENABLED!!\n");
|
||||
/* Update configuration value */
|
||||
pci_read_config_byte_united(PIIX4_dev, PIIX4_bus, PIIX4_devfn,
|
||||
SMBHSTCFG, &temp);
|
||||
/* Note: We test the bit again in the next 'if' just to be sure... */
|
||||
}
|
||||
#endif /* FORCE_PIIX4_ENABLE */
|
||||
|
||||
if ((temp & 1) == 0) {
|
||||
printk("SMBUS: Error: Host SMBus controller not enabled!\n");
|
||||
error_return=-ENODEV;
|
||||
goto END;
|
||||
}
|
||||
|
||||
/* Everything is happy, let's grab the memory and set things up. */
|
||||
request_region(piix4_smba, 8, "piix4");
|
||||
|
||||
#ifdef DEBUG
|
||||
if ((temp & 0x0E) == 8)
|
||||
printk("piix4.o: PIIX4 using Interrupt 9 for SMBus.\n");
|
||||
else if ((temp & 0x0E) == 0)
|
||||
printk("piix4.o: PIIX4 using Interrupt SMI# for SMBus.\n");
|
||||
else
|
||||
printk("piix4.o: PIIX4: Illegal Interrupt configuration (or code out "
|
||||
"of date)!\n");
|
||||
|
||||
pci_read_config_byte_united(PIIX4_dev, PIIX4_bus, PIIX4_devfn, SMBREV,
|
||||
&temp);
|
||||
printk("piix4.o: SMBREV = 0x%X\n",temp);
|
||||
printk("piix4.o: PIIX4_smba = 0x%X\n",piix4_smba);
|
||||
#endif /* DEBUG */
|
||||
|
||||
END:
|
||||
return error_return;
|
||||
}
|
||||
|
||||
|
||||
/* Internally used pause function */
|
||||
void piix4_do_pause( unsigned int amount )
|
||||
{
|
||||
current->state = TASK_INTERRUPTIBLE;
|
||||
schedule_timeout(amount);
|
||||
}
|
||||
|
||||
/* Another internally used function */
|
||||
int piix4_transaction(void)
|
||||
{
|
||||
int temp;
|
||||
int result=0;
|
||||
int timeout=0;
|
||||
|
||||
#ifdef DEBUG
|
||||
printk("piix4.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, "
|
||||
"DAT1=%02x\n",
|
||||
inb_p(SMBHSTCNT),inb_p(SMBHSTCMD),inb_p(SMBHSTADD),inb_p(SMBHSTDAT0),
|
||||
inb_p(SMBHSTDAT1));
|
||||
#endif
|
||||
|
||||
/* Make sure the SMBus host is ready to start transmitting */
|
||||
if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
|
||||
#ifdef DEBUG
|
||||
printk("piix4.o: SMBus busy (%02x). Resetting... \n",temp);
|
||||
#endif
|
||||
outb_p(temp, SMBHSTSTS);
|
||||
if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
|
||||
#ifdef DEBUG
|
||||
printk("piix4.o: Failed! (%02x)\n",temp);
|
||||
#endif
|
||||
return -1;
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
printk("piix4.o: Successfull!\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* start the transaction by setting bit 6 */
|
||||
outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT);
|
||||
|
||||
/* We will always wait for a fraction of a second! (See PIIX4 docs errata) */
|
||||
do {
|
||||
piix4_do_pause(1);
|
||||
temp=inb_p(SMBHSTSTS);
|
||||
} while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
|
||||
|
||||
/* If the SMBus is still busy, we give up */
|
||||
if (timeout >= MAX_TIMEOUT) {
|
||||
#ifdef DEBUG
|
||||
printk("piix4.o: SMBus Timeout!\n");
|
||||
result = -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (temp & 0x10) {
|
||||
result = -1;
|
||||
#ifdef DEBUG
|
||||
printk("piix4.o: Error: Failed bus transaction\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (temp & 0x08) {
|
||||
result = -1;
|
||||
printk("piix4.o: Bus collision! SMBus may be locked until next hard
|
||||
reset. (sorry!)\n");
|
||||
/* Clock stops and slave is stuck in mid-transmission */
|
||||
}
|
||||
|
||||
if (temp & 0x04) {
|
||||
result = -1;
|
||||
#ifdef DEBUG
|
||||
printk("piix4.o: Error: no response!\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (inb_p(SMBHSTSTS) != 0x00)
|
||||
outb_p( inb(SMBHSTSTS), SMBHSTSTS);
|
||||
|
||||
if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
|
||||
#ifdef DEBUG
|
||||
printk("piix4.o: Failed reset at end of transaction (%02x)\n",temp);
|
||||
#endif
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printk("piix4.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, "
|
||||
"DAT0=%02x, DAT1=%02x\n",
|
||||
inb_p(SMBHSTCNT),inb_p(SMBHSTCMD),inb_p(SMBHSTADD),inb_p(SMBHSTDAT0),
|
||||
inb_p(SMBHSTDAT1));
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Return -1 on error. See smbus.h for more information */
|
||||
s32 piix4_access(u8 addr, char read_write,
|
||||
u8 command, int size, union smbus_data * data)
|
||||
{
|
||||
int i,len;
|
||||
|
||||
switch(size) {
|
||||
case SMBUS_PROC_CALL:
|
||||
printk("piix4.o: SMBUS_PROC_CALL not supported!\n");
|
||||
return -1;
|
||||
case SMBUS_QUICK:
|
||||
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD);
|
||||
size = PIIX4_QUICK;
|
||||
break;
|
||||
case SMBUS_BYTE:
|
||||
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD);
|
||||
if (read_write == SMBUS_WRITE)
|
||||
outb_p(command, SMBHSTCMD);
|
||||
size = PIIX4_BYTE;
|
||||
break;
|
||||
case SMBUS_BYTE_DATA:
|
||||
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD);
|
||||
outb_p(command, SMBHSTCMD);
|
||||
if (read_write == SMBUS_WRITE)
|
||||
outb_p(data->byte,SMBHSTDAT0);
|
||||
size = PIIX4_BYTE_DATA;
|
||||
break;
|
||||
case SMBUS_WORD_DATA:
|
||||
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD);
|
||||
outb_p(command, SMBHSTCMD);
|
||||
if (read_write == SMBUS_WRITE) {
|
||||
outb_p(data->word & 0xff,SMBHSTDAT0);
|
||||
outb_p((data->word & 0xff00) >> 8,SMBHSTDAT1);
|
||||
}
|
||||
size = PIIX4_WORD_DATA;
|
||||
break;
|
||||
case SMBUS_BLOCK_DATA:
|
||||
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD);
|
||||
outb_p(command, SMBHSTCMD);
|
||||
if (read_write == SMBUS_WRITE) {
|
||||
len = data->block[0];
|
||||
if (len < 0)
|
||||
len = 0;
|
||||
if (len > 32)
|
||||
len = 32;
|
||||
outb_p(len,SMBHSTDAT0);
|
||||
i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
|
||||
for (i = 1; i <= len; i ++)
|
||||
outb_p(data->block[i],SMBBLKDAT);
|
||||
}
|
||||
size = PIIX4_BLOCK_DATA;
|
||||
break;
|
||||
}
|
||||
|
||||
outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT);
|
||||
|
||||
if (piix4_transaction()) /* Error in transaction */
|
||||
return -1;
|
||||
|
||||
if ((read_write == SMBUS_WRITE) || (size == PIIX4_QUICK))
|
||||
return 0;
|
||||
|
||||
|
||||
switch(size) {
|
||||
case PIIX4_BYTE: /* Where is the result put? I assume here it is in
|
||||
SMBHSTDAT0 but it might just as well be in the
|
||||
SMBHSTCMD. No clue in the docs */
|
||||
|
||||
data->byte = inb_p(SMBHSTDAT0);
|
||||
break;
|
||||
case PIIX4_BYTE_DATA:
|
||||
data->byte = inb_p(SMBHSTDAT0);
|
||||
break;
|
||||
case PIIX4_WORD_DATA:
|
||||
data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
|
||||
break;
|
||||
case PIIX4_BLOCK_DATA:
|
||||
data->block[0] = inb_p(SMBHSTDAT0);
|
||||
i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
|
||||
for (i = 1; i <= data->block[0]; i++)
|
||||
data->block[i] = inb_p(SMBBLKDAT);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int piix4_init(void)
|
||||
{
|
||||
int res;
|
||||
printk("piix4.o version %s (%s)\n",LM_VERSION,LM_DATE);
|
||||
#ifdef DEBUG
|
||||
/* PE- It might be good to make this a permanent part of the code! */
|
||||
if (piix4_initialized) {
|
||||
printk("piix4.o: Oops, piix4_init called a second time!\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
#endif
|
||||
piix4_initialized = 0;
|
||||
if ((res = piix4_setup())) {
|
||||
printk("piix4.o: PIIX4 not detected, module not inserted.\n");
|
||||
piix4_cleanup();
|
||||
return res;
|
||||
}
|
||||
piix4_initialized ++;
|
||||
sprintf(piix4_adapter.name,"SMBus PIIX4 adapter at %04x",piix4_smba);
|
||||
piix4_adapter.id = ALGO_SMBUS | SMBUS_PIIX4;
|
||||
piix4_adapter.algo = &smbus_algorithm;
|
||||
piix4_adapter.smbus_access = &piix4_access;
|
||||
if ((res = smbus_add_adapter(&piix4_adapter))) {
|
||||
printk("piix4.o: Adapter registration failed, module not inserted.\n");
|
||||
piix4_cleanup();
|
||||
return res;
|
||||
}
|
||||
piix4_initialized++;
|
||||
printk("piix4.o: PIIX4 bus detected and initialized\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int piix4_cleanup(void)
|
||||
{
|
||||
int res;
|
||||
if (piix4_initialized >= 2)
|
||||
{
|
||||
if ((res = smbus_del_adapter(&piix4_adapter))) {
|
||||
printk("piix4.o: smbus_del_adapter failed, module not removed\n");
|
||||
return res;
|
||||
} else
|
||||
piix4_initialized--;
|
||||
}
|
||||
if (piix4_initialized >= 1) {
|
||||
release_region(piix4_smba, 8);
|
||||
piix4_initialized--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef MODULE
|
||||
|
||||
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
|
||||
MODULE_DESCRIPTION("PIIX4 SMBus driver");
|
||||
|
||||
int init_module(void)
|
||||
{
|
||||
return piix4_init();
|
||||
}
|
||||
|
||||
int cleanup_module(void)
|
||||
{
|
||||
return piix4_cleanup();
|
||||
}
|
||||
|
||||
#endif /* MODULE */
|
||||
|
||||
-617
@@ -1,617 +0,0 @@
|
||||
/*
|
||||
sensors.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
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 <linux/ctype.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
#include "version.h"
|
||||
#include "compat.h"
|
||||
#include "i2c.h"
|
||||
#include "isa.h"
|
||||
#include "sensors.h"
|
||||
|
||||
|
||||
#ifdef MODULE
|
||||
extern int init_module(void);
|
||||
extern int cleanup_module(void);
|
||||
#endif /* MODULE */
|
||||
|
||||
static int sensors_create_name(char **name, const char *prefix,
|
||||
struct i2c_adapter * adapter, int addr);
|
||||
static void sensors_parse_reals(int *nrels, void *buffer, int bufsize,
|
||||
long *results, int magnitude);
|
||||
static void sensors_write_reals(int nrels,void *buffer,int *bufsize,
|
||||
long *results, int magnitude);
|
||||
static int sensors_proc_chips(ctl_table *ctl, int write, struct file * filp,
|
||||
void *buffer, size_t *lenp);
|
||||
static int sensors_sysctl_chips (ctl_table *table, int *name, int nlen,
|
||||
void *oldval, size_t *oldlenp, void *newval,
|
||||
size_t newlen, void **context);
|
||||
|
||||
static int sensors_init(void);
|
||||
static int sensors_cleanup(void);
|
||||
|
||||
#define SENSORS_ENTRY_MAX 20
|
||||
static struct ctl_table_header *sensors_entries[SENSORS_ENTRY_MAX];
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58))
|
||||
static struct i2c_client *sensors_clients[SENSORS_ENTRY_MAX];
|
||||
static unsigned short sensors_inodes[SENSORS_ENTRY_MAX];
|
||||
static void sensors_fill_inode(struct inode *inode, int fill);
|
||||
static void sensors_dir_fill_inode(struct inode *inode, int fill);
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */
|
||||
|
||||
static ctl_table sysctl_table[] = {
|
||||
{ CTL_DEV, "dev", NULL, 0, 0555 },
|
||||
{ 0 },
|
||||
{ DEV_SENSORS, "sensors", NULL, 0, 0555 },
|
||||
{ 0 },
|
||||
{ 0, NULL, NULL, 0, 0555 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static ctl_table sensors_proc_dev_sensors[] = {
|
||||
{ SENSORS_CHIPS, "chips", NULL, 0, 0644, NULL, &sensors_proc_chips,
|
||||
&sensors_sysctl_chips },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static ctl_table sensors_proc_dev[] = {
|
||||
{ DEV_SENSORS, "sensors", NULL, 0, 0555, sensors_proc_dev_sensors },
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
|
||||
static ctl_table sensors_proc[] = {
|
||||
{ CTL_DEV, "dev", NULL, 0, 0555, sensors_proc_dev },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
||||
static struct ctl_table_header *sensors_proc_header;
|
||||
static int sensors_initialized;
|
||||
|
||||
/* This returns a nice name for a new directory; for example lm78-isa-0310
|
||||
(for a LM78 chip on the ISA bus at port 0x310), or lm75-i2c-3-4e (for
|
||||
a LM75 chip on the third i2c bus at address 0x4e).
|
||||
name is allocated first. */
|
||||
int sensors_create_name(char **name, const char *prefix,
|
||||
struct i2c_adapter * adapter, int addr)
|
||||
{
|
||||
char name_buffer[50];
|
||||
int id;
|
||||
if (i2c_is_isa_adapter(adapter))
|
||||
sprintf(name_buffer,"%s-isa-%04x",prefix,addr);
|
||||
else {
|
||||
if ((id = i2c_adapter_id(adapter)) < 0)
|
||||
return -ENOENT;
|
||||
sprintf(name_buffer,"%s-i2c-%d-%02x",prefix,id,addr);
|
||||
}
|
||||
*name = kmalloc(strlen(name_buffer)+1,GFP_KERNEL);
|
||||
strcpy(*name,name_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This rather complex function must be called when you want to add an entry
|
||||
to /proc/sys/dev/sensors/chips (not yet implemented). It also creates
|
||||
a new directory within /proc/sys/dev/sensors/.
|
||||
ctl_template should be a template of the newly created directory. It is
|
||||
copied in memory. The extra2 field of each file is set to point to client.
|
||||
If any driver wants subdirectories within the newly created directory,
|
||||
this function must be updated! */
|
||||
int sensors_register_entry(struct i2c_client *client ,const char *prefix,
|
||||
ctl_table *ctl_template)
|
||||
{
|
||||
int i,res,len,id;
|
||||
ctl_table *new_table;
|
||||
char *name;
|
||||
struct ctl_table_header *new_header;
|
||||
|
||||
if ((res = sensors_create_name(&name,prefix,client->adapter,
|
||||
i2c_is_isa_client(client)?
|
||||
((struct isa_client *) client)->isa_addr:
|
||||
client->addr)))
|
||||
return res;
|
||||
|
||||
for (id = 0; id < SENSORS_ENTRY_MAX; id++)
|
||||
if (! sensors_entries[id]) {
|
||||
break;
|
||||
}
|
||||
if (id == SENSORS_ENTRY_MAX) {
|
||||
kfree(name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
id += 256;
|
||||
|
||||
len = 0;
|
||||
while (ctl_template[len].procname)
|
||||
len++;
|
||||
len += 7;
|
||||
if (! (new_table = kmalloc(sizeof(ctl_table) * len,GFP_KERNEL))) {
|
||||
kfree(name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(new_table,sysctl_table,6 * sizeof(ctl_table));
|
||||
new_table[0].child = &new_table[2];
|
||||
new_table[2].child = &new_table[4];
|
||||
new_table[4].child = &new_table[6];
|
||||
new_table[4].procname = name;
|
||||
new_table[4].ctl_name = id;
|
||||
memcpy(new_table+6,ctl_template,(len-6) * sizeof(ctl_table));
|
||||
for (i = 6; i < len; i++)
|
||||
new_table[i].extra2 = client;
|
||||
|
||||
if (! (new_header = register_sysctl_table(new_table,0))) {
|
||||
kfree(new_table);
|
||||
kfree(name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
sensors_entries[id-256] = new_header;
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58))
|
||||
sensors_clients[id-256] = client;
|
||||
#ifdef DEBUG
|
||||
if (!new_header || !new_header->ctl_table ||
|
||||
!new_header->ctl_table->child ||
|
||||
!new_header->ctl_table->child->child ||
|
||||
!new_header->ctl_table->child->child->de) {
|
||||
printk("sensors.o: NULL pointer when trying to install fill_inode fix!\n");
|
||||
return id;
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
sensors_inodes[id-256] = new_header->ctl_table->child->child->de->low_ino;
|
||||
new_header->ctl_table->child->child->de->fill_inode = &sensors_dir_fill_inode;
|
||||
#endif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58))
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void sensors_deregister_entry(int id)
|
||||
{
|
||||
ctl_table *table;
|
||||
id -= 256;
|
||||
if (sensors_entries[id]) {
|
||||
table = sensors_entries[id]->ctl_table;
|
||||
unregister_sysctl_table(sensors_entries[id]);
|
||||
kfree((void *) (table[4].procname));
|
||||
kfree(table);
|
||||
sensors_entries[id] = NULL;
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58))
|
||||
sensors_clients[id] = NULL;
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */
|
||||
}
|
||||
}
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58))
|
||||
/* Monitor access for /proc/sys/dev/sensors; make unloading sensors.o
|
||||
impossible if some process still uses it or some file in it */
|
||||
void sensors_fill_inode(struct inode *inode, int fill)
|
||||
{
|
||||
if (fill)
|
||||
MOD_INC_USE_COUNT;
|
||||
else
|
||||
MOD_DEC_USE_COUNT;
|
||||
}
|
||||
|
||||
/* Monitor access for /proc/sys/dev/sensors/ directories; make unloading
|
||||
the corresponding module impossible if some process still uses it or
|
||||
some file in it */
|
||||
void sensors_dir_fill_inode(struct inode *inode, int fill)
|
||||
{
|
||||
int i;
|
||||
struct i2c_client *client;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (! inode) {
|
||||
printk("sensors.o: Warning: inode NULL in fill_inode()\n");
|
||||
return;
|
||||
}
|
||||
#endif /* def DEBUG */
|
||||
|
||||
for (i = 0; i < SENSORS_ENTRY_MAX; i++)
|
||||
if (sensors_clients[i] && (sensors_inodes[i] == inode->i_ino))
|
||||
break;
|
||||
#ifdef DEBUG
|
||||
if (i == SENSORS_ENTRY_MAX) {
|
||||
printk("sensors.o: Warning: inode (%ld) not found in fill_inode()\n",
|
||||
inode->i_ino);
|
||||
return;
|
||||
}
|
||||
#endif /* def DEBUG */
|
||||
client = sensors_clients[i];
|
||||
if (fill)
|
||||
client->driver->inc_use(client);
|
||||
else
|
||||
client->driver->dec_use(client);
|
||||
}
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */
|
||||
|
||||
int sensors_proc_chips(ctl_table *ctl, int write, struct file * filp,
|
||||
void *buffer, size_t *lenp)
|
||||
{
|
||||
char BUF[SENSORS_PREFIX_MAX + 30];
|
||||
int buflen,curbufsize,i;
|
||||
struct ctl_table *client_tbl;
|
||||
|
||||
if (write)
|
||||
return 0;
|
||||
|
||||
/* If buffer is size 0, or we try to read when not at the start, we
|
||||
return nothing. Note that I think writing when not at the start
|
||||
does not work either, but anyway, this is straight from the kernel
|
||||
sources. */
|
||||
if (!*lenp || (filp->f_pos && !write)) {
|
||||
*lenp = 0;
|
||||
return 0;
|
||||
}
|
||||
curbufsize = 0;
|
||||
for (i = 0; i < SENSORS_ENTRY_MAX; i ++)
|
||||
if (sensors_entries[i]) {
|
||||
client_tbl = sensors_entries[i]->ctl_table->child->child;
|
||||
buflen = sprintf(BUF,"%d\t%s\n",client_tbl->ctl_name,
|
||||
client_tbl->procname);
|
||||
if (buflen + curbufsize > *lenp)
|
||||
buflen=*lenp-curbufsize;
|
||||
copy_to_user(buffer,BUF,buflen);
|
||||
curbufsize += buflen;
|
||||
(char *) buffer += buflen;
|
||||
}
|
||||
*lenp = curbufsize;
|
||||
filp->f_pos += curbufsize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sensors_sysctl_chips (ctl_table *table, int *name, int nlen, void *oldval,
|
||||
size_t *oldlenp, void *newval, size_t newlen,
|
||||
void **context)
|
||||
{
|
||||
struct sensors_chips_data data;
|
||||
int i,oldlen,nrels,maxels;
|
||||
struct ctl_table *client_tbl;
|
||||
|
||||
if (oldval && oldlenp && ! get_user_data(oldlen,oldlenp) && oldlen) {
|
||||
maxels = oldlen / sizeof(struct sensors_chips_data);
|
||||
nrels = 0;
|
||||
for (i = 0; (i < SENSORS_ENTRY_MAX) && (nrels < maxels); i++)
|
||||
if (sensors_entries[i]) {
|
||||
client_tbl = sensors_entries[i]->ctl_table->child->child;
|
||||
data.sysctl_id = client_tbl->ctl_name;
|
||||
strcpy(data.name,client_tbl->procname);
|
||||
copy_to_user(oldval,&data,sizeof(struct sensors_chips_data));
|
||||
(char *) oldval += sizeof(struct sensors_chips_data);
|
||||
nrels++;
|
||||
}
|
||||
oldlen = nrels * sizeof(struct sensors_chips_data);
|
||||
put_user(oldlen,oldlenp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* This funcion reads or writes a 'real' value (encoded by the combination
|
||||
of an integer and a magnitude, the last is the power of ten the value
|
||||
should be divided with) to a /proc/sys directory. To use this function,
|
||||
you must (before registering the ctl_table) set the extra2 field to the
|
||||
client, and the extra1 field to a function of the form:
|
||||
void func(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
This function can be called for three values of operation. If operation
|
||||
equals SENSORS_PROC_REAL_INFO, the magnitude should be returned in
|
||||
nrels_mag. If operation equals SENSORS_PROC_REAL_READ, values should
|
||||
be read into results. nrels_mag should return the number of elements
|
||||
read; the maximum number is put in it on entry. Finally, if operation
|
||||
equals SENSORS_PROC_REAL_WRITE, the values in results should be
|
||||
written to the chip. nrels_mag contains on entry the number of elements
|
||||
found.
|
||||
In all cases, client points to the client we wish to interact with,
|
||||
and ctl_name is the SYSCTL id of the file we are accessing. */
|
||||
int sensors_proc_real(ctl_table *ctl, int write, struct file * filp,
|
||||
void *buffer, size_t *lenp)
|
||||
{
|
||||
#define MAX_RESULTS 20
|
||||
int mag,nrels=MAX_RESULTS;
|
||||
long results[MAX_RESULTS];
|
||||
sensors_real_callback callback = ctl -> extra1;
|
||||
struct i2c_client *client = ctl -> extra2;
|
||||
|
||||
/* If buffer is size 0, or we try to read when not at the start, we
|
||||
return nothing. Note that I think writing when not at the start
|
||||
does not work either, but anyway, this is straight from the kernel
|
||||
sources. */
|
||||
if (!*lenp || (filp->f_pos && !write)) {
|
||||
*lenp = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the magnitude */
|
||||
callback(client,SENSORS_PROC_REAL_INFO,ctl->ctl_name,&mag,NULL);
|
||||
|
||||
if (write) {
|
||||
/* Read the complete input into results, converting to longs */
|
||||
sensors_parse_reals(&nrels,buffer,*lenp,results,mag);
|
||||
|
||||
if (! nrels)
|
||||
return 0;
|
||||
|
||||
/* Now feed this information back to the client */
|
||||
callback(client,SENSORS_PROC_REAL_WRITE,ctl->ctl_name,&nrels,results);
|
||||
|
||||
filp->f_pos += *lenp;
|
||||
return 0;
|
||||
} else { /* read */
|
||||
/* Get the information from the client into results */
|
||||
callback(client,SENSORS_PROC_REAL_READ,ctl->ctl_name,&nrels,results);
|
||||
|
||||
/* And write them to buffer, converting to reals */
|
||||
sensors_write_reals(nrels,buffer,lenp,results,mag);
|
||||
filp->f_pos += *lenp;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* This function is equivalent to sensors_proc_real, only it interacts with
|
||||
the sysctl(2) syscall, and returns no reals, but integers */
|
||||
int sensors_sysctl_real (ctl_table *table, int *name, int nlen, void *oldval,
|
||||
size_t *oldlenp, void *newval, size_t newlen,
|
||||
void **context)
|
||||
{
|
||||
long results[MAX_RESULTS];
|
||||
int oldlen,nrels=MAX_RESULTS;
|
||||
sensors_real_callback callback = table -> extra1;
|
||||
struct i2c_client *client = table -> extra2;
|
||||
|
||||
/* Check if we need to output the old values */
|
||||
if (oldval && oldlenp && ! get_user_data(oldlen,oldlenp) && oldlen) {
|
||||
callback(client,SENSORS_PROC_REAL_READ,table->ctl_name,&nrels,results);
|
||||
|
||||
/* Note the rounding factor! */
|
||||
if (nrels * sizeof(long) < oldlen)
|
||||
oldlen = nrels * sizeof(long);
|
||||
oldlen = (oldlen / sizeof(long)) * sizeof(long);
|
||||
copy_to_user(oldval,results,oldlen);
|
||||
put_user(oldlen,oldlenp);
|
||||
}
|
||||
|
||||
if (newval && newlen) {
|
||||
/* Note the rounding factor! */
|
||||
newlen -= newlen % sizeof(long);
|
||||
nrels = newlen / sizeof(long);
|
||||
copy_from_user(results,newval,newlen);
|
||||
|
||||
/* Get the new values back to the client */
|
||||
callback(client,SENSORS_PROC_REAL_WRITE,table->ctl_name,&nrels,results);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* nrels contains initially the maximum number of elements which can be
|
||||
put in results, and finally the number of elements actually put there.
|
||||
A magnitude of 1 will multiply everything with 10; etc.
|
||||
buffer, bufsize is the character buffer we read from and its length.
|
||||
results will finally contain the parsed integers.
|
||||
|
||||
Buffer should contain several reals, separated by whitespace. A real
|
||||
has the following syntax:
|
||||
[ Minus ] Digit* [ Dot Digit* ]
|
||||
(everything between [] is optional; * means zero or more).
|
||||
When the next character is unparsable, everything is skipped until the
|
||||
next whitespace.
|
||||
|
||||
WARNING! This is tricky code. I have tested it, but there may still be
|
||||
hidden bugs in it, even leading to crashes and things!
|
||||
*/
|
||||
void sensors_parse_reals(int *nrels, void *buffer, int bufsize,
|
||||
long *results, int magnitude)
|
||||
{
|
||||
int maxels,min,mag;
|
||||
long res;
|
||||
char nextchar=0;
|
||||
|
||||
maxels = *nrels;
|
||||
*nrels = 0;
|
||||
|
||||
while (bufsize && (*nrels < maxels)) {
|
||||
|
||||
/* Skip spaces at the start */
|
||||
while (bufsize && ! get_user_data(nextchar,(char *) buffer) &&
|
||||
isspace((int) nextchar)) {
|
||||
bufsize --;
|
||||
((char *) buffer)++;
|
||||
}
|
||||
|
||||
/* Well, we may be done now */
|
||||
if (! bufsize)
|
||||
return;
|
||||
|
||||
/* New defaults for our result */
|
||||
min = 0;
|
||||
res = 0;
|
||||
mag = magnitude;
|
||||
|
||||
/* Check for a minus */
|
||||
if (! get_user_data(nextchar,(char *) buffer) && (nextchar == '-')) {
|
||||
min=1;
|
||||
bufsize--;
|
||||
((char *) buffer)++;
|
||||
}
|
||||
|
||||
/* Digits before a decimal dot */
|
||||
while (bufsize && !get_user_data(nextchar,(char *) buffer) &&
|
||||
isdigit((int) nextchar)) {
|
||||
res = res * 10 + nextchar - '0';
|
||||
bufsize--;
|
||||
((char *) buffer)++;
|
||||
}
|
||||
|
||||
/* If mag < 0, we must actually divide here! */
|
||||
while (mag < 0) {
|
||||
res = res / 10;
|
||||
mag++;
|
||||
}
|
||||
|
||||
if (bufsize && (nextchar == '.')) {
|
||||
/* Skip the dot */
|
||||
bufsize--;
|
||||
((char *) buffer)++;
|
||||
|
||||
/* Read digits while they are significant */
|
||||
while(bufsize && (mag > 0) &&
|
||||
!get_user_data(nextchar,(char *) buffer) &&
|
||||
isdigit((int) nextchar)) {
|
||||
res = res * 10 + nextchar - '0';
|
||||
mag--;
|
||||
bufsize--;
|
||||
((char *) buffer)++;
|
||||
}
|
||||
}
|
||||
/* If we are out of data, but mag > 0, we need to scale here */
|
||||
while (mag > 0) {
|
||||
res = res * 10;
|
||||
mag --;
|
||||
}
|
||||
|
||||
/* Skip everything until we hit whitespace */
|
||||
while(bufsize && !get_user_data(nextchar,(char *) buffer) &&
|
||||
isspace ((int) nextchar)) {
|
||||
bufsize --;
|
||||
((char *) buffer) ++;
|
||||
}
|
||||
|
||||
/* Put res in results */
|
||||
results[*nrels] = (min?-1:1)*res;
|
||||
(*nrels)++;
|
||||
}
|
||||
|
||||
/* Well, there may be more in the buffer, but we need no more data.
|
||||
Ignore anything that is left. */
|
||||
return;
|
||||
}
|
||||
|
||||
void sensors_write_reals(int nrels,void *buffer,int *bufsize,long *results,
|
||||
int magnitude)
|
||||
{
|
||||
#define BUFLEN 20
|
||||
char BUF[BUFLEN+1]; /* An individual representation should fit in here! */
|
||||
char printfstr[10];
|
||||
int nr=0;
|
||||
int buflen,mag,times;
|
||||
int curbufsize=0;
|
||||
|
||||
while ((nr < nrels) && (curbufsize < *bufsize)) {
|
||||
mag=magnitude;
|
||||
|
||||
if (nr != 0) {
|
||||
put_user(' ', (char *) buffer);
|
||||
curbufsize ++;
|
||||
((char *) buffer) ++;
|
||||
}
|
||||
|
||||
/* Fill BUF with the representation of the next string */
|
||||
if (mag <= 0) {
|
||||
|
||||
buflen=sprintf(BUF,"%ld",results[nr]);
|
||||
if (buflen < 0) { /* Oops, a sprintf error! */
|
||||
*bufsize=0;
|
||||
return;
|
||||
}
|
||||
while ((mag < 0) && (buflen < BUFLEN)) {
|
||||
BUF[buflen++]='0';
|
||||
mag++;
|
||||
}
|
||||
BUF[buflen]=0;
|
||||
} else {
|
||||
times=1;
|
||||
for (times=1; mag-- > 0; times *= 10);
|
||||
if (results[nr] < 0) {
|
||||
BUF[0] = '-';
|
||||
buflen = 1;
|
||||
} else
|
||||
buflen=0;
|
||||
strcpy(printfstr,"%ld.%0Xld");
|
||||
printfstr[6]=magnitude+'0';
|
||||
buflen+=sprintf(BUF+buflen,printfstr,abs(results[nr])/times,
|
||||
abs(results[nr])%times);
|
||||
if (buflen < 0) { /* Oops, a sprintf error! */
|
||||
*bufsize=0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now copy it to the user-space buffer */
|
||||
if (buflen + curbufsize > *bufsize)
|
||||
buflen=*bufsize-curbufsize;
|
||||
copy_to_user(buffer,BUF,buflen);
|
||||
curbufsize += buflen;
|
||||
(char *) buffer += buflen;
|
||||
|
||||
nr ++;
|
||||
}
|
||||
if (curbufsize < *bufsize) {
|
||||
put_user('\n', (char *) buffer);
|
||||
curbufsize ++;
|
||||
}
|
||||
*bufsize=curbufsize;
|
||||
}
|
||||
|
||||
int sensors_init(void)
|
||||
{
|
||||
printk("sensors.o version %s (%s)\n",LM_VERSION,LM_DATE);
|
||||
sensors_initialized = 0;
|
||||
if (! (sensors_proc_header = register_sysctl_table(sensors_proc,0)))
|
||||
return -ENOMEM;
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58))
|
||||
sensors_proc_header->ctl_table->child->de->fill_inode = &sensors_fill_inode;
|
||||
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */
|
||||
sensors_initialized ++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sensors_cleanup(void)
|
||||
{
|
||||
if (sensors_initialized >= 1) {
|
||||
unregister_sysctl_table(sensors_proc_header);
|
||||
sensors_initialized --;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef MODULE
|
||||
|
||||
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
|
||||
MODULE_DESCRIPTION("LM78 driver");
|
||||
|
||||
int init_module(void)
|
||||
{
|
||||
return sensors_init();
|
||||
}
|
||||
|
||||
int cleanup_module(void)
|
||||
{
|
||||
return sensors_cleanup();
|
||||
}
|
||||
|
||||
#endif /* MODULE */
|
||||
|
||||
-253
@@ -1,253 +0,0 @@
|
||||
/*
|
||||
sensors.h - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef SENSORS_SENSORS_H
|
||||
#define SENSORS_SENSORS_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
/* Next two must be included before sysctl.h can be included, in 2.0 kernels */
|
||||
#include <linux/types.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sysctl.h>
|
||||
|
||||
/* The type of callback functions used in sensors_{proc,sysctl}_real */
|
||||
typedef void (*sensors_real_callback) (struct i2c_client *client,
|
||||
int operation, int ctl_name,
|
||||
int *nrels_mag, long *results);
|
||||
|
||||
/* Values for the operation field in the above function type */
|
||||
#define SENSORS_PROC_REAL_INFO 1
|
||||
#define SENSORS_PROC_REAL_READ 2
|
||||
#define SENSORS_PROC_REAL_WRITE 3
|
||||
|
||||
/* These funcion reads or writes a 'real' value (encoded by the combination
|
||||
of an integer and a magnitude, the last is the power of ten the value
|
||||
should be divided with) to a /proc/sys directory. To use these functions,
|
||||
you must (before registering the ctl_table) set the extra2 field to the
|
||||
client, and the extra1 field to a function of the form:
|
||||
void func(struct i2c_client *client, int operation, int ctl_name,
|
||||
int *nrels_mag, long *results)
|
||||
This last function can be called for three values of operation. If
|
||||
operation equals SENSORS_PROC_REAL_INFO, the magnitude should be returned
|
||||
in nrels_mag. If operation equals SENSORS_PROC_REAL_READ, values should
|
||||
be read into results. nrels_mag should return the number of elements
|
||||
read; the maximum number is put in it on entry. Finally, if operation
|
||||
equals SENSORS_PROC_REAL_WRITE, the values in results should be
|
||||
written to the chip. nrels_mag contains on entry the number of elements
|
||||
found.
|
||||
In all cases, client points to the client we wish to interact with,
|
||||
and ctl_name is the SYSCTL id of the file we are accessing. */
|
||||
extern int sensors_sysctl_real (ctl_table *table, int *name, int nlen,
|
||||
void *oldval, size_t *oldlenp, void *newval,
|
||||
size_t newlen, void **context);
|
||||
extern int sensors_proc_real(ctl_table *ctl, int write, struct file * filp,
|
||||
void *buffer, size_t *lenp);
|
||||
|
||||
|
||||
|
||||
/* These rather complex functions must be called when you want to add or
|
||||
delete an entry in /proc/sys/dev/sensors/chips (not yet implemented). It
|
||||
also creates a new directory within /proc/sys/dev/sensors/.
|
||||
ctl_template should be a template of the newly created directory. It is
|
||||
copied in memory. The extra2 field of each file is set to point to client.
|
||||
If any driver wants subdirectories within the newly created directory,
|
||||
these functions must be updated! */
|
||||
extern int sensors_register_entry(struct i2c_client *client,
|
||||
const char *prefix, ctl_table *ctl_template);
|
||||
extern void sensors_deregister_entry(int id);
|
||||
|
||||
#endif /* def __KERNEL__ */
|
||||
|
||||
|
||||
/* The maximum length of the prefix */
|
||||
#define SENSORS_PREFIX_MAX 20
|
||||
|
||||
/* Driver IDs */
|
||||
#define I2C_DRIVERID_I2CDEV 1000
|
||||
#define I2C_DRIVERID_I2CPROC 1001
|
||||
#define I2C_DRIVERID_LM78 1002
|
||||
#define I2C_DRIVERID_LM75 1003
|
||||
#define I2C_DRIVERID_GL518 1004
|
||||
#define I2C_DRIVERID_EEPROM 1005
|
||||
#define I2C_DRIVERID_W83781D 1006
|
||||
#define I2C_DRIVERID_LM80 1007
|
||||
#define I2C_DRIVERID_ADM1021 1008
|
||||
#define I2C_DRIVERID_ADM9240 1009
|
||||
#define I2C_DRIVERID_LTC1710 1010
|
||||
|
||||
/* Sysctl IDs */
|
||||
#ifdef DEV_HWMON
|
||||
#define DEV_SENSORS DEV_HWMON
|
||||
#else /* ndef DEV_HWMOM */
|
||||
#define DEV_SENSORS 2 /* The id of the lm_sensors directory within the
|
||||
dev table */
|
||||
#endif /* def DEV_HWMON */
|
||||
|
||||
#define SENSORS_CHIPS 1
|
||||
struct sensors_chips_data {
|
||||
int sysctl_id;
|
||||
char name[SENSORS_PREFIX_MAX + 13];
|
||||
};
|
||||
|
||||
#define LM78_SYSCTL_IN0 1000 /* Volts * 100 */
|
||||
#define LM78_SYSCTL_IN1 1001
|
||||
#define LM78_SYSCTL_IN2 1002
|
||||
#define LM78_SYSCTL_IN3 1003
|
||||
#define LM78_SYSCTL_IN4 1004
|
||||
#define LM78_SYSCTL_IN5 1005
|
||||
#define LM78_SYSCTL_IN6 1006
|
||||
#define LM78_SYSCTL_FAN1 1101 /* Rotations/min */
|
||||
#define LM78_SYSCTL_FAN2 1102
|
||||
#define LM78_SYSCTL_FAN3 1103
|
||||
#define LM78_SYSCTL_TEMP 1200 /* Degrees Celcius * 10 */
|
||||
#define LM78_SYSCTL_VID 1300 /* Volts * 100 */
|
||||
#define LM78_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */
|
||||
#define LM78_SYSCTL_ALARMS 2001 /* bitvector */
|
||||
|
||||
#define LM78_ALARM_IN0 0x0001
|
||||
#define LM78_ALARM_IN1 0x0002
|
||||
#define LM78_ALARM_IN2 0x0004
|
||||
#define LM78_ALARM_IN3 0x0008
|
||||
#define LM78_ALARM_IN4 0x0100
|
||||
#define LM78_ALARM_IN5 0x0200
|
||||
#define LM78_ALARM_IN6 0x0400
|
||||
#define LM78_ALARM_FAN1 0x0040
|
||||
#define LM78_ALARM_FAN2 0x0080
|
||||
#define LM78_ALARM_FAN3 0x0800
|
||||
#define LM78_ALARM_TEMP 0x0010
|
||||
#define LM78_ALARM_BTI 0x0020
|
||||
#define LM78_ALARM_CHAS 0x1000
|
||||
#define LM78_ALARM_FIFO 0x2000
|
||||
#define LM78_ALARM_SMI_IN 0x4000
|
||||
|
||||
#define W83781D_SYSCTL_IN0 1000 /* Volts * 100 */
|
||||
#define W83781D_SYSCTL_IN1 1001
|
||||
#define W83781D_SYSCTL_IN2 1002
|
||||
#define W83781D_SYSCTL_IN3 1003
|
||||
#define W83781D_SYSCTL_IN4 1004
|
||||
#define W83781D_SYSCTL_IN5 1005
|
||||
#define W83781D_SYSCTL_IN6 1006
|
||||
#define W83781D_SYSCTL_FAN1 1101 /* Rotations/min */
|
||||
#define W83781D_SYSCTL_FAN2 1102
|
||||
#define W83781D_SYSCTL_FAN3 1103
|
||||
#define W83781D_SYSCTL_TEMP1 1200 /* Degrees Celcius * 10 */
|
||||
#define W83781D_SYSCTL_TEMP2 1201 /* Degrees Celcius * 10 */
|
||||
#define W83781D_SYSCTL_TEMP3 1202 /* Degrees Celcius * 10 */
|
||||
#define W83781D_SYSCTL_VID 1300 /* Volts * 100 */
|
||||
#define W83781D_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */
|
||||
#define W83781D_SYSCTL_ALARMS 2001 /* bitvector */
|
||||
#define W83781D_SYSCTL_BEEP 2002 /* bitvector */
|
||||
|
||||
#define W83781D_ALARM_IN0 0x0001
|
||||
#define W83781D_ALARM_IN1 0x0002
|
||||
#define W83781D_ALARM_IN2 0x0004
|
||||
#define W83781D_ALARM_IN3 0x0008
|
||||
#define W83781D_ALARM_IN4 0x0100
|
||||
#define W83781D_ALARM_IN5 0x0200
|
||||
#define W83781D_ALARM_IN6 0x0400
|
||||
#define W83781D_ALARM_FAN1 0x0040
|
||||
#define W83781D_ALARM_FAN2 0x0080
|
||||
#define W83781D_ALARM_FAN3 0x0800
|
||||
#define W83781D_ALARM_TEMP1 0x0010
|
||||
#define W83781D_ALARM_TEMP23 0x0020
|
||||
#define W83781D_ALARM_CHAS 0x1000
|
||||
|
||||
#define LM75_SYSCTL_TEMP 1200 /* Degrees Celcius * 10 */
|
||||
|
||||
#define ADM1021_SYSCTL_TEMP 1200
|
||||
#define ADM1021_SYSCTL_REMOTE_TEMP 1201
|
||||
#define ADM1021_SYSCTL_DIE_CODE 1202
|
||||
#define ADM1021_SYSCTL_STATUS 1203
|
||||
|
||||
#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_VID 1300
|
||||
#define GL518_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */
|
||||
#define GL518_SYSCTL_ALARMS 2001 /* bitvector */
|
||||
#define GL518_SYSCTL_BEEP 2002 /* 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
|
||||
|
||||
#define EEPROM_SYSCTL1 1000
|
||||
#define EEPROM_SYSCTL2 1001
|
||||
#define EEPROM_SYSCTL3 1002
|
||||
#define EEPROM_SYSCTL4 1003
|
||||
#define EEPROM_SYSCTL5 1004
|
||||
#define EEPROM_SYSCTL6 1005
|
||||
#define EEPROM_SYSCTL7 1006
|
||||
#define EEPROM_SYSCTL8 1007
|
||||
|
||||
#define LM80_SYSCTL_IN0 1000 /* Volts * 100 */
|
||||
#define LM80_SYSCTL_IN1 1001
|
||||
#define LM80_SYSCTL_IN2 1002
|
||||
#define LM80_SYSCTL_IN3 1003
|
||||
#define LM80_SYSCTL_IN4 1004
|
||||
#define LM80_SYSCTL_IN5 1005
|
||||
#define LM80_SYSCTL_IN6 1006
|
||||
#define LM80_SYSCTL_FAN1 1101 /* Rotations/min */
|
||||
#define LM80_SYSCTL_FAN2 1102
|
||||
#define LM80_SYSCTL_TEMP 1250 /* Degrees Celcius * 100 */
|
||||
#define LM80_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */
|
||||
#define LM80_SYSCTL_ALARMS 2001 /* bitvector */
|
||||
|
||||
#define ADM9240_SYSCTL_IN0 1000 /* Volts * 100 */
|
||||
#define ADM9240_SYSCTL_IN1 1001
|
||||
#define ADM9240_SYSCTL_IN2 1002
|
||||
#define ADM9240_SYSCTL_IN3 1003
|
||||
#define ADM9240_SYSCTL_IN4 1004
|
||||
#define ADM9240_SYSCTL_IN5 1005
|
||||
#define ADM9240_SYSCTL_FAN1 1101 /* Rotations/min */
|
||||
#define ADM9240_SYSCTL_FAN2 1102
|
||||
#define ADM9240_SYSCTL_TEMP 1250 /* Degrees Celcius * 100 */
|
||||
#define ADM9240_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */
|
||||
#define ADM9240_SYSCTL_ALARMS 2001 /* bitvector */
|
||||
#define ADM9240_SYSCTL_ANALOG_OUT 2002
|
||||
#define ADM9240_SYSCTL_VID 2003
|
||||
|
||||
#define LTC1710_SYSCTL_SWITCH_1 1000
|
||||
#define LTC1710_SYSCTL_SWITCH_2 1001
|
||||
|
||||
#define LM80_ALARM_IN0 0x0001
|
||||
#define LM80_ALARM_IN1 0x0002
|
||||
#define LM80_ALARM_IN2 0x0004
|
||||
#define LM80_ALARM_IN3 0x0008
|
||||
#define LM80_ALARM_IN4 0x0010
|
||||
#define LM80_ALARM_IN5 0x0020
|
||||
#define LM80_ALARM_IN6 0x0040
|
||||
#define LM80_ALARM_FAN1 0x0400
|
||||
#define LM80_ALARM_FAN2 0x0800
|
||||
#define LM80_ALARM_TEMP_HOT 0x0100
|
||||
#define LM80_ALARM_TEMP_OS 0x2000
|
||||
#define LM80_ALARM_CHAS 0x1000
|
||||
#define LM80_ALARM_BTI 0x0200
|
||||
#define LM80_ALARM_INT_IN 0x0080
|
||||
#endif /* def SENSORS_SENSORS_H */
|
||||
-277
@@ -1,277 +0,0 @@
|
||||
/*
|
||||
smbus.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
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/kernel.h>
|
||||
#include <linux/stddef.h>
|
||||
|
||||
#include "i2c.h"
|
||||
#ifdef SPINLOCK
|
||||
#include <asm/spinlock.h>
|
||||
#else
|
||||
#include <asm/semaphore.h>
|
||||
#endif
|
||||
|
||||
#include "version.h"
|
||||
#include "smbus.h"
|
||||
|
||||
static s32 smbus_access_i2c (struct i2c_adapter * adapter, u8 addr,
|
||||
char read_write, u8 command, int size,
|
||||
union smbus_data * data);
|
||||
|
||||
static int smbus_master_xfer (struct smbus_adapter *adap,
|
||||
struct i2c_msg msgs[], int num);
|
||||
static int smbus_slave_send (struct smbus_adapter *adap, char *data, int len);
|
||||
static int smbus_slave_recv (struct smbus_adapter *adap, char *data, int len);
|
||||
static int smbus_algo_control (struct smbus_adapter *adap, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
static int smbus_client_register (struct smbus_client *client);
|
||||
static int smbus_client_unregister (struct smbus_client *client);
|
||||
|
||||
static int smbus_init(void);
|
||||
static int smbus_cleanup(void);
|
||||
|
||||
#ifdef MODULE
|
||||
extern int init_module(void);
|
||||
extern int cleanup_module(void);
|
||||
#endif /* MODULE */
|
||||
|
||||
/* This is the actual algorithm we define */
|
||||
struct smbus_algorithm smbus_algorithm = {
|
||||
/* name */ "Non-I2C SMBus adapter",
|
||||
/* id */ ALGO_SMBUS,
|
||||
/* master_xfer */ &smbus_master_xfer,
|
||||
/* slave_send */ &smbus_slave_send,
|
||||
/* slave_rcv */ &smbus_slave_recv,
|
||||
/* algo_control */ &smbus_algo_control,
|
||||
/* client_register */ &smbus_client_register,
|
||||
/* client_unregister*/&smbus_client_unregister
|
||||
};
|
||||
|
||||
|
||||
/* OK, so you want to access a bus using the SMBus protocols. Well, it either
|
||||
is registered as a SMBus-only adapter (like the PIIX4), or we need to
|
||||
simulate the SMBus commands using the i2c access routines.
|
||||
We do all locking here, so you can ignore that in the adapter-specific
|
||||
smbus_accesss routine. */
|
||||
s32 smbus_access (struct i2c_adapter * adapter, u8 addr, char read_write,
|
||||
u8 command, int size, union smbus_data * data)
|
||||
{
|
||||
int res;
|
||||
if ((adapter->id & ALGO_MASK) == ALGO_SMBUS) {
|
||||
#ifdef SPINLOCK
|
||||
spin_lock_irqsave(&adapter->lock,adapter->lockflags);
|
||||
#else
|
||||
down(&adapter->lock);
|
||||
#endif
|
||||
res = ((struct smbus_adapter *) adapter) ->
|
||||
smbus_access(addr,read_write,command,size,data);
|
||||
#ifdef SPINLOCK
|
||||
spin_unlock_irqrestore(&adapter->lock,adapter->lockflags);
|
||||
#else
|
||||
up(&adapter->lock);
|
||||
#endif
|
||||
} else
|
||||
res = smbus_access_i2c(adapter,addr,read_write,command,size,data);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Simulate a SMBus command using the i2c protocol
|
||||
No checking of paramters is done! */
|
||||
s32 smbus_access_i2c(struct i2c_adapter * adapter, u8 addr, char read_write,
|
||||
u8 command, int size, union smbus_data * data)
|
||||
{
|
||||
/* So we need to generate a series of msgs. In the case of writing, we
|
||||
need to use only one message; when reading, we need two. We initialize
|
||||
most things with sane defaults, to keep the code below somewhat
|
||||
simpler. */
|
||||
char msgbuf0[33];
|
||||
char msgbuf1[33];
|
||||
int num = read_write == SMBUS_READ?2:1;
|
||||
struct i2c_msg msg[2] = { { addr, 0, 1, msgbuf0 },
|
||||
{ addr, I2C_M_RD, 0, msgbuf1 }
|
||||
};
|
||||
int i;
|
||||
|
||||
msgbuf0[0] = command;
|
||||
switch(size) {
|
||||
case SMBUS_QUICK:
|
||||
msg[0].len = 0;
|
||||
num = 1; /* Special case: The read/write field is used as data */
|
||||
break;
|
||||
case SMBUS_BYTE:
|
||||
if (read_write == SMBUS_READ)
|
||||
/* Special case: only a read! */
|
||||
msg[0].flags = I2C_M_RD;
|
||||
break;
|
||||
case SMBUS_BYTE_DATA:
|
||||
if (read_write == SMBUS_READ)
|
||||
msg[1].len = 1;
|
||||
else {
|
||||
msg[0].len = 2;
|
||||
msgbuf0[1] = data->byte;
|
||||
}
|
||||
break;
|
||||
case SMBUS_WORD_DATA:
|
||||
if (read_write == SMBUS_READ)
|
||||
msg[1].len = 2;
|
||||
else {
|
||||
msg[0].len=3;
|
||||
msgbuf0[1] = data->word & 0xff;
|
||||
msgbuf0[2] = (data->word >> 8) & 0xff;
|
||||
}
|
||||
break;
|
||||
case SMBUS_PROC_CALL:
|
||||
num = 2; /* Special case */
|
||||
msg[0].len = 3;
|
||||
msg[1].len = 2;
|
||||
msgbuf0[1] = data->word & 0xff;
|
||||
msgbuf0[2] = (data->word >> 8) & 0xff;
|
||||
break;
|
||||
case SMBUS_BLOCK_DATA:
|
||||
if (read_write == SMBUS_READ) {
|
||||
printk("smbus.o: Block read not supported under I2C emulation!\n");
|
||||
return -1;
|
||||
} else {
|
||||
msg[1].len = data->block[0] + 1;
|
||||
if (msg[1].len > 32) {
|
||||
printk("smbus.o: smbus_access called with invalid block write "
|
||||
"size (%d)\n",msg[1].len);
|
||||
return -1;
|
||||
}
|
||||
for (i = 1; i <= msg[1].len; i++)
|
||||
msgbuf0[i] = data->block[1];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printk("smbus.o: smbus_access called with invalid size (%d)\n",size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (i2c_transfer(adapter, msg, num) < 0)
|
||||
return -1;
|
||||
|
||||
if(read_write == SMBUS_READ)
|
||||
switch(size) {
|
||||
case SMBUS_BYTE:
|
||||
data->byte = msgbuf0[0];
|
||||
break;
|
||||
case SMBUS_BYTE_DATA:
|
||||
data->byte = msgbuf1[0];
|
||||
break;
|
||||
case SMBUS_WORD_DATA:
|
||||
case SMBUS_PROC_CALL:
|
||||
data->word = msgbuf1[0] + (msgbuf1[1] << 8);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Algorithm master_xfer call-back implementation. Can't do that... */
|
||||
int smbus_master_xfer (struct smbus_adapter *adap, struct i2c_msg msgs[],
|
||||
int num)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printk("smbus.o: smbus_master_xfer called for adapter `%s' "
|
||||
"(no i2c level access possible!)\n",
|
||||
adap->name);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Algorithm slave_send call-back implementation. Can't do that... */
|
||||
int smbus_slave_send (struct smbus_adapter *adap, char *data, int len)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printk("smbus.o: smbus_slave_send called for adapter `%s' "
|
||||
"(no i2c level access possible!)\n",
|
||||
adap->name);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Algorithm slave_recv call-back implementation. Can't do that... */
|
||||
int smbus_slave_recv (struct smbus_adapter *adap, char *data, int len)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printk("smbus.o: smbus_slave_recv called for adapter `%s' "
|
||||
"(no i2c level access possible!)\n",
|
||||
adap->name);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Here we can put additional calls to modify the workings of the algorithm.
|
||||
But right now, there is no need for that. */
|
||||
int smbus_algo_control (struct smbus_adapter *adap, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ehm... This is called when a client is registered to an adapter. We could
|
||||
do all kinds of neat stuff here like, ehm - returning success? */
|
||||
int smbus_client_register (struct smbus_client *client)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smbus_client_unregister (struct smbus_client *client)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smbus_init(void)
|
||||
{
|
||||
int res;
|
||||
printk("smbus.o version %s (%s)\n",LM_VERSION,LM_DATE);
|
||||
if ((res = smbus_add_algorithm(&smbus_algorithm)))
|
||||
printk("smbus.o: Algorithm registration failed, module not inserted.\n");
|
||||
else
|
||||
printk("smbus.o initialized\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
int smbus_cleanup(void)
|
||||
{
|
||||
int res;
|
||||
if ((res = smbus_del_algorithm(&smbus_algorithm)))
|
||||
printk("smbus.o: Algorithm deregistration failed, module not removed\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
/* OK, this will for now _only_ compile as a module, but this is neat for
|
||||
later, if we want to compile it straight into the kernel */
|
||||
#ifdef MODULE
|
||||
|
||||
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
|
||||
MODULE_DESCRIPTION("System Management Bus (SMBus) access");
|
||||
|
||||
int init_module(void)
|
||||
{
|
||||
return smbus_init();
|
||||
}
|
||||
|
||||
int cleanup_module(void)
|
||||
{
|
||||
return smbus_cleanup();
|
||||
}
|
||||
|
||||
#endif /* MODULE */
|
||||
-300
@@ -1,300 +0,0 @@
|
||||
/*
|
||||
smbus.h - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef SENSORS_SMBUS_H
|
||||
#define SENSORS_SMBUS_H
|
||||
|
||||
/* This file must interface with Simon Vogl's i2c driver. Version 19981006 is
|
||||
OK, earlier versions are not; later versions will probably give problems
|
||||
too.
|
||||
*/
|
||||
#include <asm/types.h>
|
||||
|
||||
/* This union is used within smbus_access routines */
|
||||
union smbus_data {
|
||||
__u8 byte;
|
||||
__u16 word;
|
||||
__u8 block[32];
|
||||
};
|
||||
|
||||
/* smbus_access read or write markers */
|
||||
#define SMBUS_READ 1
|
||||
#define SMBUS_WRITE 0
|
||||
|
||||
/* SMBus transaction types (size parameter in the above functions)
|
||||
Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */
|
||||
#define SMBUS_QUICK 0
|
||||
#define SMBUS_BYTE 1
|
||||
#define SMBUS_BYTE_DATA 2
|
||||
#define SMBUS_WORD_DATA 3
|
||||
#define SMBUS_PROC_CALL 4
|
||||
#define SMBUS_BLOCK_DATA 5
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
/* SPINLOCK is defined in i2c.h. */
|
||||
#ifdef SPINLOCK
|
||||
#include <asm/spinlock.h>
|
||||
#else
|
||||
#include <asm/semaphore.h>
|
||||
#endif
|
||||
|
||||
#ifdef LM_SENSORS
|
||||
#include "i2c.h"
|
||||
#else
|
||||
#include <linux/i2c.h>
|
||||
#endif
|
||||
|
||||
/* Declarations, to keep the compiler happy */
|
||||
struct smbus_driver;
|
||||
struct smbus_client;
|
||||
struct smbus_algorithm;
|
||||
struct smbus_adapter;
|
||||
union smbus_data;
|
||||
|
||||
/* A driver tells us how we should handle a specific kind of chip.
|
||||
A specific instance of such a chip is called a client.
|
||||
This structure is essentially the same as i2c_driver. */
|
||||
struct smbus_driver {
|
||||
char name[32];
|
||||
int id;
|
||||
unsigned int flags;
|
||||
int (* attach_adapter) (struct smbus_adapter *);
|
||||
int (* detach_client) (struct smbus_client *);
|
||||
int (* command) (struct smbus_client *, unsigned int cmd, void *arg);
|
||||
void (* inc_use) (struct smbus_client *);
|
||||
void (* dec_use) (struct smbus_client *);
|
||||
};
|
||||
|
||||
/* A client is a specifc instance of a chip: for each detected chip, there will
|
||||
be a client. Its operation is controlled by a driver.
|
||||
This structure is essentially the same as i2c_client. */
|
||||
struct smbus_client {
|
||||
char name[32];
|
||||
int id;
|
||||
unsigned int flags;
|
||||
unsigned char addr;
|
||||
struct smbus_adapter *adapter;
|
||||
struct smbus_driver *driver;
|
||||
void *data;
|
||||
};
|
||||
|
||||
/* An algorithm describes how a certain class of busses can be accessed.
|
||||
A specific instance of sucj a bus is called an adapter.
|
||||
This structure is essentially the same as i2c_adapter. */
|
||||
struct smbus_algorithm {
|
||||
char name[32];
|
||||
unsigned int id;
|
||||
int (* master_xfer) (struct smbus_adapter *adap, struct i2c_msg msgs[],
|
||||
int num);
|
||||
int (* slave_send) (struct smbus_adapter *,char *, int);
|
||||
int (* slave_recv) (struct smbus_adapter *,char *, int);
|
||||
int (* algo_control) (struct smbus_adapter *, unsigned int, unsigned long);
|
||||
int (* client_register) (struct smbus_client *);
|
||||
int (* client_unregister) (struct smbus_client *);
|
||||
};
|
||||
|
||||
/* An adapter is a specifc instance of a bus: for each detected bus, there will
|
||||
be an adapter. Its operation is controlled by an algorithm.
|
||||
SPINLOCK must be the same as declared in i2c.h.
|
||||
This structure is an extension of i2c_algorithm. */
|
||||
struct smbus_adapter {
|
||||
char name[32];
|
||||
unsigned int id;
|
||||
struct smbus_algorithm *algo;
|
||||
void *data;
|
||||
#ifdef SPINLOCK
|
||||
spinlock_t lock;
|
||||
unsigned long lockflags;
|
||||
#else
|
||||
struct semaphore lock;
|
||||
#endif
|
||||
unsigned int flags;
|
||||
struct smbus_client *clients[I2C_CLIENT_MAX];
|
||||
int client_count;
|
||||
int timeout;
|
||||
int retries;
|
||||
|
||||
/* Here ended i2c_adapter */
|
||||
s32 (* smbus_access) (u8 addr, char read_write,
|
||||
u8 command, int size, union smbus_data * data);
|
||||
};
|
||||
|
||||
/* We need to mark SMBus algorithms in the algorithm structure.
|
||||
Note that any and all adapters using a non-i2c driver use in this
|
||||
setup ALGO_SMBUS. Adapters define their own smbus access routine.
|
||||
This also means that adapter->smbus_access is only available if
|
||||
this flag is set! */
|
||||
#define ALGO_SMBUS 0x40000
|
||||
|
||||
/* SMBus Adapter ids */
|
||||
#define SMBUS_PIIX4 1
|
||||
|
||||
/* Detect whether we are on an SMBus-only bus. Note that if this returns
|
||||
false, you can still use the smbus access routines, as these emulate
|
||||
the SMBus on I2C. Unless they are undefined on your algorithm, of
|
||||
course. */
|
||||
#define i2c_is_smbus_client(clientptr) \
|
||||
((clientptr)->adapter->algo->id == ALGO_SMBUS)
|
||||
#define i2c_is_smbus_adapter(adapptr) \
|
||||
((adapptr)->algo->id == ALGO_SMBUS)
|
||||
|
||||
|
||||
/* Declare an algorithm structure. All SMBus derived adapters should use this
|
||||
algorithm! */
|
||||
extern struct smbus_algorithm smbus_algorithm;
|
||||
|
||||
/* This is the very generalized SMBus access routine. You probably do not
|
||||
want to use this, though; one of the functions below may be much easier,
|
||||
and probably just as fast.
|
||||
Note that we use i2c_adapter here, because you do not need a specific
|
||||
smbus adapter to call this function. */
|
||||
extern s32 smbus_access (struct i2c_adapter * adapter, u8 addr,
|
||||
char read_write, u8 command, int size,
|
||||
union smbus_data * data);
|
||||
|
||||
/* Now follow the 'nice' access routines. These also document the calling
|
||||
conventions of smbus_access. */
|
||||
|
||||
extern inline s32 smbus_write_quick(struct i2c_adapter * adapter, u8 addr,
|
||||
u8 value)
|
||||
{
|
||||
return smbus_access(adapter,addr,value,0,SMBUS_QUICK,NULL);
|
||||
}
|
||||
|
||||
extern inline s32 smbus_read_byte(struct i2c_adapter * adapter,u8 addr)
|
||||
{
|
||||
union smbus_data data;
|
||||
if (smbus_access(adapter,addr,SMBUS_READ,0,SMBUS_BYTE,&data))
|
||||
return -1;
|
||||
else
|
||||
return 0x0FF & data.byte;
|
||||
}
|
||||
|
||||
extern inline s32 smbus_write_byte(struct i2c_adapter * adapter, u8 addr,
|
||||
u8 value)
|
||||
{
|
||||
return smbus_access(adapter,addr,SMBUS_WRITE,value, SMBUS_BYTE,NULL);
|
||||
}
|
||||
|
||||
extern inline s32 smbus_read_byte_data(struct i2c_adapter * adapter,
|
||||
u8 addr, u8 command)
|
||||
{
|
||||
union smbus_data data;
|
||||
if (smbus_access(adapter,addr,SMBUS_READ,command,SMBUS_BYTE_DATA,&data))
|
||||
return -1;
|
||||
else
|
||||
return 0x0FF & data.byte;
|
||||
}
|
||||
|
||||
extern inline s32 smbus_write_byte_data(struct i2c_adapter * adapter,
|
||||
u8 addr, u8 command, u8 value)
|
||||
{
|
||||
union smbus_data data;
|
||||
data.byte = value;
|
||||
return smbus_access(adapter,addr,SMBUS_WRITE,command,SMBUS_BYTE_DATA,&data);
|
||||
}
|
||||
|
||||
extern inline s32 smbus_read_word_data(struct i2c_adapter * adapter,
|
||||
u8 addr, u8 command)
|
||||
{
|
||||
union smbus_data data;
|
||||
if (smbus_access(adapter,addr,SMBUS_READ,command,SMBUS_WORD_DATA,&data))
|
||||
return -1;
|
||||
else
|
||||
return 0x0FFFF & data.word;
|
||||
}
|
||||
|
||||
extern inline s32 smbus_write_word_data(struct i2c_adapter * adapter,
|
||||
u8 addr, u8 command, u16 value)
|
||||
{
|
||||
union smbus_data data;
|
||||
data.word = value;
|
||||
return smbus_access(adapter,addr,SMBUS_WRITE,command,SMBUS_WORD_DATA,&data);
|
||||
}
|
||||
|
||||
extern inline s32 smbus_process_call(struct i2c_adapter * adapter,
|
||||
u8 addr, u8 command, u16 value)
|
||||
{
|
||||
union smbus_data data;
|
||||
data.word = value;
|
||||
if (smbus_access(adapter,addr,SMBUS_WRITE,command,SMBUS_PROC_CALL,&data))
|
||||
return -1;
|
||||
else
|
||||
return 0x0FFFF & data.word;
|
||||
}
|
||||
|
||||
/* Returns the number of read bytes */
|
||||
extern inline s32 smbus_read_block_data(struct i2c_adapter * adapter,
|
||||
u8 addr, u8 command, u8 *values)
|
||||
{
|
||||
union smbus_data data;
|
||||
int i;
|
||||
if (smbus_access(adapter,addr,SMBUS_READ,command,SMBUS_BLOCK_DATA,&data))
|
||||
return -1;
|
||||
else {
|
||||
for (i = 1; i <= data.block[0]; i++)
|
||||
values[i-1] = data.block[i];
|
||||
return data.block[0];
|
||||
}
|
||||
}
|
||||
|
||||
extern inline s32 smbus_write_block_data(struct i2c_adapter * adapter,
|
||||
u8 addr, u8 command, u8 length,
|
||||
u8 *values)
|
||||
{
|
||||
union smbus_data data;
|
||||
int i;
|
||||
if (length > 32)
|
||||
length = 32;
|
||||
for (i = 1; i <= length; i++)
|
||||
data.block[i] = values[i-1];
|
||||
data.block[0] = length;
|
||||
return smbus_access(adapter,addr,SMBUS_WRITE,command,SMBUS_BLOCK_DATA,&data);
|
||||
}
|
||||
|
||||
|
||||
/* Next: define SMBus variants of registering. */
|
||||
|
||||
#define smbus_add_algorithm(algoptr) \
|
||||
i2c_add_algorithm((struct i2c_algorithm *) (algoptr))
|
||||
#define smbus_del_algorithm(algoptr) \
|
||||
i2c_del_algorithm((struct i2c_algorithm *) (algoptr))
|
||||
|
||||
#define smbus_add_adapter(adapptr) \
|
||||
i2c_add_adapter((struct i2c_adapter *) (adapptr))
|
||||
#define smbus_del_adapter(adapptr) \
|
||||
i2c_del_adapter((struct i2c_adapter *) (adapptr))
|
||||
|
||||
#define smbus_add_driver(driverptr) \
|
||||
i2c_add_driver((struct i2c_driver *) (driverptr))
|
||||
#define smbus_del_driver(driverptr) \
|
||||
i2c_add_driver((struct i2c_driver *) (driverptr))
|
||||
|
||||
#define smbus_attach_client(clientptr) \
|
||||
i2c_attach_client((struct i2c_client *) (clientptr))
|
||||
#define smbus_detach_client(clientptr) \
|
||||
i2c_detach_client((struct i2c_client *) (clientptr))
|
||||
|
||||
|
||||
#endif /* def __KERNEL__ */
|
||||
|
||||
#endif /* ndef SENSORS_SMBUS_H */
|
||||
|
||||
-1127
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user