From: j.w.r.degoede@hhs.nl (Hans de Goede)
To: lm-sensors@vger.kernel.org
Subject: [lm-sensors] New Abit uGuru driver + libsensors patch, review
Date: Fri, 21 Oct 2005 20:20:40 +0000 [thread overview]
Message-ID: <43592F7E.300@hhs.nl> (raw)
In-Reply-To: <4358B291.6070702@hhs.nl>
Ah,
Now I understand, Thunderbird somehow chooses application/octet as type
for C-source code. Reattaching this time as .txt file.
About patch vs c-code file, I don't have any changes to the kernel build
system, one of the blessings of the new buildsystem is that it is very
easy to build modules out of tree and that is what I've been doing I've
also attached a Makefile.txt for you to build it with.
Thanks & Regards,
Hans
Jean Delvare wrote:
>>>>The driver.
>>>
>>>-ENOPATCH
>>
>>Yes, its a new driver, so nothing to diff against, would a diff against
>>/dev/null really make you happier?
>
>
> No, I mean, there is no attachement at all.
>
> And yes, a patch would still make me happier, because it would include
> the relevant changes to the kernel build system so that we can check
> that your code compiles properly, and people could test your driver
> more easily too.
>
>
>>Can someone please take a look at the driver and give me some proper
>>feedback?
>
>
> Not before you actually send something to us ;)
>
> Note that the mailing list is configured to drop any unacceptable
> attachement. Maybe that's what happened. Accepted types are text/plain
> and text/x-patch. Make sure your attachement has the proper type. If
> you think there is a different type we should accept, please let us
> know.
>
-------------- next part --------------
/*
abituguru.c - Part of lm_sensors, Linux kernel modules
for hardware monitoring
Copyright (c) 2005 Hans de Goede <j.w.r.degoede@hhs.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 driver supports the sensor part of the custom Abit uGuru chip found
on Abit uGuru motherboards. Currently it has been tested on an Abit KV8
Pro, AV8, AX8 and AN8-SLI. The openguru program this driver is based on
has also been successfully tested on: Abit AV8 3rd Eye, AN8 Ultra and AN7.
Unfortunatly there are no specs for this chip, this driver is based on the
(reverse enginered) openguru program by Olle Sandberg <ollebull@gmail.com>.
You can find the first version of his program which only supports reading
the sensors not the min/max values at:
http://hem.bredband.net/b330708/oguru/oguru.tar.gz
I would like to express my thanks to Olle, this driver couldnot have been
written without his efforts.
Because of the lack of specs only the sensors part of the uGuru is
supported. A word of caution to those who want to experiment and see if
they can figure the voltage / clock programming out, I tried reading and
only reading banks 0-0x30 with the reading code used for the sensor banks
(0x20-0x28) and this resulted in a permanent reprogramming of the voltages,
luckily I had the sensors part configured so that it would shutdown my
system on any out of spec voltages which proprably safed my computer (after
a reboot I managed to immediatly enter the bios and reload the defaults).
This probably means that the read/write cycle for the non sensor part is
different from the sensor part.
This driver used via686a.c as a skeleleton since that is an ISA only chip
like the Abit uGuru (AFAIK).
*/
/* History:
version 0.9:
-initial release, only reading the sensors is supported.
version 0.9.1:
-Olle has done some more reverse engineering, so now reading the min/max
values and the settings is also supported.
-modified the driver to work with the new hwmon seperation in 2.6.13,
As a result this version requires 2.6.13 or higher.
-simplified the ready code.
-added a abituguru_wait function which waits for the uguru the reach
a certain state, use this everywhere instead of having seperate loops
with there own timeouts scatered over the place.
-use array sysfs initalisation instead of #define hell.
-fix rounding of values.
version 0.9.2:
-Olle has now reverse engineered the writing of the flags and min/max
values, so now writing the min/max values is also supported.
-Now that we've seen testing of more mainboards, it turns out that
the way the volt inputs are hooked up isn't identical for all mainboards,
also causing the ranges for the volt inputs to be not constant. Because
of this conversion for the voltage sensors has been removed, the raw
register values are now returned. Sample entries for known mainboards
will be added to sensors.conf .
-During this testing, it has also been shown that certain addresses in
bank1 (0x0D) sometimes are attached to a temp sensor and sometimes to a
volt sensor. Luckily with some tricks this can be detected, so now we
detect if a sensor is connected and what type for all addresses in bank1.
version 0.9.3:
-userspace expects temp and in values to be in milli units, so they are now
multiplied by 1000 in show and divided by 1000 on set.
-Now that the min/max values can be written I've been able to trigger some
false alarms and have managed to find out how the alarms work, so now
reading the alarms is supported also.
version 0.9.4:
-reshuffle / cleanup (mainly bank1 handling)
-fix some race conditions
version 0.9.5:
-Reading/writing the settings (enable alarm, beep, shutdown) is now also
supported.
version 1.0.0:
-Added support for controling the fan PWM (speed control).
-Add force param to skip "detection" .
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/i2c-isa.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/init.h>
#include <asm/io.h>
/* This is needed untill this gets merged upstream */
#ifndef __SENSOR_DEVICE_ATTR
#define __SENSOR_DEVICE_ATTR(_name,_mode,_show,_store,_index) \
{ .dev_attr = __ATTR(_name,_mode,_show,_store), \
.index = _index, \
}
#endif
/* Debugging output is enabled for now, since this driver is still in the
testing fase */
#define ABIT_UGURU_DEBUG 1
/* Banks */
#define ABIT_UGURU_ALARM_BANK 0x20 /* 3 bytes, 2 bytes bank1, 1 byte bank2 */
#define ABIT_UGURU_SENSOR_BANK1 0x21 /* volt and temp */
#define ABIT_UGURU_FAN_PWM 0x24 /* 3x 5 bytes */
#define ABIT_UGURU_SENSOR_BANK2 0x26 /* fans */
/* uGuru sensor bank 1 flags */ /* Alarm if: */
#define ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE 0x01 /* temp over warn */
#define ABIT_UGURU_VOLT_HIGH_ALARM_ENABLE 0x02 /* volt over max */
#define ABIT_UGURU_VOLT_LOW_ALARM_ENABLE 0x04 /* volt under min */
#define ABIT_UGURU_TEMP_HIGH_ALARM_FLAG 0x10 /* temp is over warn */
#define ABIT_UGURU_VOLT_HIGH_ALARM_FLAG 0x20 /* volt is over max */
#define ABIT_UGURU_VOLT_LOW_ALARM_FLAG 0x40 /* volt is under min */
/* uGuru sensor bank 2 flags */ /* Alarm if: */
#define ABIT_UGURU_FAN_LOW_ALARM_ENABLE 0x01 /* fan under min */
/* uGuru sensor bank common flags */
#define ABIT_UGURU_BEEP_ENABLE 0x08 /* beep if alarm */
#define ABIT_UGURU_SHUTDOWN_ENABLE 0x80 /* shutdown if alarm */
/* uGuru fan PWM (speed control) flags */
#define ABIT_UGURU_FAN_PWM_ENABLE 0x80 /* enable speed control */
/* Values used for conversion */
#define ABIT_UGURU_FAN_MAX 15300 /* RPM */
/* Bank1 sensor types */
#define ABIT_UGURU_IN_SENSOR 0
#define ABIT_UGURU_TEMP_SENSOR 1
#define ABIT_UGURU_NC 2
/* All the variables below are named identical to the oguru and oguru2 programs
reverse engineered by Olle Sandberg, hence the names might not be 100%
logical. I could come up with better names, but I prefer keeping the names
identical so that this driver can be compared with his work more easily. */
/* Two i/o-ports are used by uGuru */
#define ABIT_UGURU_BASE 0x00E0
#define ABIT_UGURU_CMD 0x00 /* Used to tell uGuru what to read and to read the actual data */
#define ABIT_UGURU_DATA 0x04 /* Mostly used to check if uGuru is busy */
/* uGuru status' */
#define ABIT_UGURU_STATUS_WRITE 0x00 /* Ready to be written */
#define ABIT_UGURU_STATUS_READ 0x01 /* Ready to be read */
#define ABIT_UGURU_STATUS_INPUT 0x08 /* More input */
#define ABIT_UGURU_STATUS_READY 0x09 /* Ready to be written */
/* Insmod parameters */
static int force;
module_param(force, bool, 0);
MODULE_PARM_DESC(force, "Set to one to force detection.");
/* For the Abit uGuru, we need to keep some data in memory.
The structure is dynamically allocated, at the same time when a new
abituguru client is allocated. */
struct abituguru_data {
struct i2c_client client;
struct class_device *class_dev;
struct semaphore update_lock;
char uguru_ready; /* is the uguru in ready state? */
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
/* Bank 1 data */
u8 bank1_sensors[2]; /* number of [0] in, [1] temp sensors */
u8 bank1_address[2][16];/* addresses of [0] in, [1] temp sensors */
/* This array holds 16 x 4 entries for all the sensors in bank 1
(flags,min,max,value for voltage/..,warn,shutdown,.. for temp). */
u8 bank1_regs[16*4];
/* The sysfs attr for the bank1 sensors are generated automaticly */
struct sensor_device_attribute bank1_attr[16*3];
/* Bank 2 data, the highest number of fans currently know to be
attached is 6, thus 6 x 3 entries (flags, min, value). */
u8 bank2_regs[6*3];
/* Alarms 2 bytes for bank1, 1 byte for bank2 */
u8 alarm_regs[3];
/* Fan PWM (speed control) 3x5 bytes */
u8 fan_pwm_regs[3*5];
};
static int abituguru_detect(struct i2c_adapter *adapter);
static int abituguru_detach_client(struct i2c_client *client);
/* Danger Will Robinson, danger! This function downs the update_lock semaphore
in the abituguru_data struct but doesn't up it, the caller must up
it when it is done with the returned data! */
static struct abituguru_data *abituguru_update_device(struct device *dev);
/* wait till the uguru is in the specified state */
static int abituguru_wait(struct i2c_client *client, u8 state)
{
/* 250 was determined by trial and error, 200 works most of the time
but not always. I assume this is cpu-speed independent, since
the ISA-bus and not the CPU should be the bottleneck. */
int lockup_cnt = 250;
while (inb_p(client->addr + ABIT_UGURU_DATA) != state)
{
lockup_cnt--;
if (lockup_cnt = 0)
return -EIO;
}
return 0;
}
/* Put the uguru in ready for input state, this code assumes that
the uguru is not already in this state.
It is the callers responsibility to make sure it isn't! */
static int abituguru_ready(struct i2c_client *client)
{
struct abituguru_data *data = i2c_get_clientdata(client);
/* Reset? / Prepare for next read/write cycle */
outb_p(0x00, client->addr + ABIT_UGURU_DATA);
/* Wait till the uguru is ready */
if (abituguru_wait(client, ABIT_UGURU_STATUS_READY))
return -EIO;
/* Cmd port MUST be read now and should contain 0xAC */
if (inb_p(client->addr + ABIT_UGURU_CMD) != 0xAC)
return -EIO;
#if ABIT_UGURU_DEBUG
/* After this the ABIT_UGURU_DATA port should contain
ABIT_UGURU_STATUS_INPUT */
if (inb_p(client->addr + ABIT_UGURU_DATA) !=
ABIT_UGURU_STATUS_INPUT)
return -EIO;
#endif
data->uguru_ready = 1;
return 0;
}
static int abituguru_send_address(struct i2c_client *client,
u8 bank_addr, u8 sensor_addr)
{
struct abituguru_data *data = i2c_get_clientdata(client);
/* When we're called the uguru normally will be ready for the first
input and thus in ABIT_UGURU_STATUS_INPUT state. If however
something went wrong previously it might not be, so then we try to
force it into ready state. If/when the uguru is ready we send the
bank address, after this the uguru is no longer "ready". */
if (!data->uguru_ready && (abituguru_ready(client) != 0))
return -EIO;
outb_p(bank_addr, client->addr + ABIT_UGURU_DATA);
data->uguru_ready = 0;
/* Wait till the uguru is ABIT_UGURU_STATUS_INPUT state again and send
the sensor addr */
if (abituguru_wait(client, ABIT_UGURU_STATUS_INPUT))
return -EIO;
outb_p(sensor_addr, client->addr + ABIT_UGURU_CMD);
return 0;
}
static int abituguru_read(struct i2c_client *client,
u8 bank_addr, u8 sensor_addr, u8 *buf, int count)
{
int bytes_read = 0;
/* Send the address */
if (abituguru_send_address(client, bank_addr, sensor_addr))
return -EIO;
/* And read the data */
while((bytes_read < count) &&
(abituguru_wait(client, ABIT_UGURU_STATUS_READ)=0)) {
buf[bytes_read] = inb(client->addr + ABIT_UGURU_CMD);
bytes_read++;
}
/* Last put the chip back in ready state */
abituguru_ready(client);
return bytes_read;
}
static int abituguru_write(struct i2c_client *client,
u8 bank_addr, u8 sensor_addr, u8 *buf, int count)
{
int bytes_written = 0;
/* Send the address */
if (abituguru_send_address(client, bank_addr, sensor_addr))
return -EIO;
/* And write the data */
while((bytes_written < count) &&
(abituguru_wait(client, ABIT_UGURU_STATUS_WRITE)=0)) {
outb(buf[bytes_written], client->addr + ABIT_UGURU_CMD);
bytes_written++;
}
/* Now we need to wait till the chip is ready to be read again,
don't ask why */
if (abituguru_wait(client, ABIT_UGURU_STATUS_READ))
return -EIO;
/* Cmd port MUST be read now and should contain 0xAC */
if (inb_p(client->addr + ABIT_UGURU_CMD) != 0xAC)
return -EIO;
/* Last put the chip back in ready state */
abituguru_ready(client);
return bytes_written;
}
/* Detect sensor type. Temp and Volt sensors are enabled with
different masks and will ignore enable masks not meant for them.
This enables us to test what kindoff sensor we're dealing with.
By setting the alarm treshholds so that we will always get an
alarm for sensor type X and then enabling the sensor as sensor type
X, if we then get an alarm it is a sensor of type X. */
static int abituguru_detect_sensor_type(struct i2c_client *client,
u8 sensor_addr)
{
u8 orig_settings[3];
u8 buf[3];
u8 val;
int ret = ABIT_UGURU_NC;
/* First read the sensor and the current settings */
if (abituguru_read(client, ABIT_UGURU_SENSOR_BANK1, sensor_addr, &val,
1) != 1) return -EIO;
if (abituguru_read(client, ABIT_UGURU_SENSOR_BANK1+1, sensor_addr,
orig_settings, 3) != 3) return -EIO;
/* Test val is sane / usable for sensor type detection. */
if ((val < 10u) || (val > 245u))
return -1; /* ? */
/* Volt sensor test, enable volt low alarm, set min value ridicously
high. If its a volt sensor this should always give us an alarm. */
buf[0]«IT_UGURU_VOLT_LOW_ALARM_ENABLE;
buf[1]%4;
buf[2]%5;
if (abituguru_write(client, ABIT_UGURU_SENSOR_BANK1+2, sensor_addr,
buf,3) != 3) return -EIO;
/* Now we need 20 ms to give the uguru time to read the sensors
and raise the alarm */
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/50);
/* Check for alarm and check the alarm is a volt low alarm. */
if ( (abituguru_read(client, ABIT_UGURU_ALARM_BANK, 0, buf, 3) = 3) &&
(buf[sensor_addr/8] & (0x01<<(sensor_addr%8)) ) &&
(abituguru_read(client, ABIT_UGURU_SENSOR_BANK1+1,
sensor_addr, buf, 3) = 3) &&
(buf[0] & ABIT_UGURU_VOLT_LOW_ALARM_FLAG) ) {
abituguru_write(client, ABIT_UGURU_SENSOR_BANK1+2,
sensor_addr, orig_settings, 3);
return ABIT_UGURU_IN_SENSOR;
}
/* Temp sensor test, enable sensor as a temp sensor, set beep value
ridicously low (but not too low, otherwise uguru ignores it).
If its a temp sensor this should always give us an alarm. */
buf[0]«IT_UGURU_TEMP_HIGH_ALARM_ENABLE;
buf[1]=5;
buf[2]\x10;
if (abituguru_write(client, ABIT_UGURU_SENSOR_BANK1+2, sensor_addr,
buf,3) != 3) return -EIO;
/* Now we need 20 ms to give the uguru time to read the sensors
and raise the alarm */
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/50);
/* Check for alarm and check the alarm is a temp high alarm. */
if ( (abituguru_read(client, ABIT_UGURU_ALARM_BANK, 0, buf, 3) = 3) &&
(buf[sensor_addr/8] & (0x01<<(sensor_addr%8)) ) &&
(abituguru_read(client, ABIT_UGURU_SENSOR_BANK1+1,
sensor_addr, buf, 3) = 3) &&
(buf[0] & ABIT_UGURU_TEMP_HIGH_ALARM_FLAG) )
ret = ABIT_UGURU_TEMP_SENSOR;
/* Restore original settings */
abituguru_write(client, ABIT_UGURU_SENSOR_BANK1+2, sensor_addr,
orig_settings, 3);
return ret;
}
/* following are the sysfs callback functions */
static ssize_t show_bank1_reg(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct abituguru_data *data = abituguru_update_device(dev);
ssize_t ret = -EIO;
if (data->valid)
ret = sprintf(buf, "%d\n", data->bank1_regs[attr->index]*1000);
up(&data->update_lock);
return ret;
}
static ssize_t show_bank2_reg(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct abituguru_data *data = abituguru_update_device(dev);
ssize_t ret = -EIO;
if (data->valid)
ret = sprintf(buf, "%d\n", (data->bank2_regs[attr->index]*
ABIT_UGURU_FAN_MAX + 128) / 255);
up(&data->update_lock);
return ret;
}
static ssize_t store_bank1_reg(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct abituguru_data *data = abituguru_update_device(dev);
int val = (simple_strtoul(buf, NULL, 10) + 500) / 1000;
ssize_t ret = -EIO;
if (data->valid && (data->bank1_regs[attr->index] != val)) {
u8 buf[3];
memcpy (buf, &data->bank1_regs[attr->index & ~3], 3);
buf[attr->index&3] = val;
if (abituguru_write(&data->client,
ABIT_UGURU_SENSOR_BANK1+2, attr->index/4, buf, 3) = 3)
ret = count;
data->valid = 0;
}
up(&data->update_lock);
return ret;
}
static ssize_t store_bank2_reg(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct abituguru_data *data = abituguru_update_device(dev);
unsigned long user_val = simple_strtoul(buf, NULL, 10);
u8 reg_val = (user_val*255 + ABIT_UGURU_FAN_MAX/2) /
ABIT_UGURU_FAN_MAX;
ssize_t ret = -EIO;
if (data->valid && (data->bank2_regs[attr->index] != reg_val)) {
u8 buf[2];
buf[0] = data->bank2_regs[(attr->index/3)*3];
buf[1] = reg_val;
if (abituguru_write(&data->client,
ABIT_UGURU_SENSOR_BANK2+2, attr->index/3, buf, 2) = 2)
ret = count;
data->valid = 0;
}
up(&data->update_lock);
return ret;
}
static ssize_t show_alarm_bank1(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct abituguru_data *data = abituguru_update_device(dev);
ssize_t ret = -EIO;
if (data->valid) {
int i, alarm = 0;
for (i=0; i<data->bank1_sensors[attr->index]; i++) {
u8 address = data->bank1_address[attr->index][i];
if (data->alarm_regs[address/8] & (0x01<<(address%8)))
alarm |= 0x01 << i;
}
ret = sprintf(buf, "%d\n", alarm);
}
up(&data->update_lock);
return ret;
}
static ssize_t show_alarm_bank2(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct abituguru_data *data = abituguru_update_device(dev);
ssize_t ret = -EIO;
if (data->valid)
ret = sprintf(buf, "%d\n", (int)data->alarm_regs[2]);
up(&data->update_lock);
return ret;
}
/* sysfs utility functions for uguru settings */
static ssize_t show_bank1_settings(struct device *dev,
struct device_attribute *devattr, u8 setting, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct abituguru_data *data = abituguru_update_device(dev);
ssize_t ret = -EIO;
if (data->valid) {
int i, mask = 0;
for (i=0; i<data->bank1_sensors[attr->index]; i++) {
u8 address = data->bank1_address[attr->index][i];
if (data->bank1_regs[address*4] & setting)
mask |= 0x01 << i;
}
ret = sprintf(buf, "%d\n", mask);
}
up(&data->update_lock);
return ret;
}
static ssize_t show_bank2_settings(struct device *dev, u8 setting, char *buf)
{
struct abituguru_data *data = abituguru_update_device(dev);
ssize_t ret = -EIO;
if (data->valid) {
int i, mask = 0;
for (i=0; i<6; i++) {
if (data->bank2_regs[i*3] & setting)
mask |= 0x01 << i;
}
ret = sprintf(buf, "%d\n", mask);
}
up(&data->update_lock);
return ret;
}
static ssize_t store_bank1_settings(struct device *dev,
struct device_attribute *devattr, u8 setting, const char *buf,
size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct abituguru_data *data = abituguru_update_device(dev);
int i, mask = simple_strtoul(buf, NULL, 10);
ssize_t ret = -EIO;
if (data->valid) {
ret = count;
for (i=0; i<data->bank1_sensors[attr->index]; i++) {
u8 buf[3], dirty = 0;
u8 address = data->bank1_address[attr->index][i];
memcpy (buf, &data->bank1_regs[address*4], 3);
/* check if we need to enable the feature */
if ((mask&(0x01<<i)) && ((buf[0]&setting)!=setting)) {
buf[0] |= setting;
dirty = 1;
}
/* check if we need to disable the feature */
if (!(mask&(0x01<<i)) && (buf[0]&setting)) {
buf[0] &= ~setting;
dirty = 1;
}
if (dirty) {
if (abituguru_write(&data->client,
ABIT_UGURU_SENSOR_BANK1+2, address,
buf, 3) != 3)
ret = -EIO;
data->valid = 0;
}
}
}
up(&data->update_lock);
return ret;
}
static ssize_t store_bank2_settings(struct device *dev, u8 setting,
const char *buf, size_t count)
{
struct abituguru_data *data = abituguru_update_device(dev);
int i, mask = simple_strtoul(buf, NULL, 10);
ssize_t ret = -EIO;
if (data->valid) {
ret = count;
for (i=0; i<6; i++) {
u8 buf[2], dirty = 0;
memcpy (buf, &data->bank2_regs[i*3], 2);
/* check if we need to enable the feature */
if ((mask&(0x01<<i)) && ((buf[0]&setting)!=setting)) {
buf[0] |= setting;
dirty = 1;
}
/* check if we need to disable the feature */
if (!(mask&(0x01<<i)) && (buf[0]&setting)) {
buf[0] &= ~setting;
dirty = 1;
}
if (dirty) {
if (abituguru_write(&data->client,
ABIT_UGURU_SENSOR_BANK2+2, i,
buf, 2) != 2)
ret = -EIO;
data->valid = 0;
}
}
}
up(&data->update_lock);
return ret;
}
/* endof sysfs utility functions for uguru settings, continuation of
normal sysfs callback functions: enables */
static ssize_t show_enable_in(struct device *dev,
struct device_attribute *devattr, char *buf)
{
return show_bank1_settings(dev, devattr,
ABIT_UGURU_VOLT_HIGH_ALARM_ENABLE|
ABIT_UGURU_VOLT_LOW_ALARM_ENABLE, buf);
}
static ssize_t show_enable_temp(struct device *dev,
struct device_attribute *devattr, char *buf)
{
return show_bank1_settings(dev, devattr,
ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE, buf);
}
static ssize_t show_enable_fan(struct device *dev,
struct device_attribute *devattr, char *buf)
{
return show_bank2_settings(dev, ABIT_UGURU_FAN_LOW_ALARM_ENABLE,
buf);
}
static ssize_t store_enable_in(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
return store_bank1_settings(dev, devattr,
ABIT_UGURU_VOLT_HIGH_ALARM_ENABLE|
ABIT_UGURU_VOLT_LOW_ALARM_ENABLE, buf, count);
}
static ssize_t store_enable_temp(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
return store_bank1_settings(dev, devattr,
ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE, buf, count);
}
static ssize_t store_enable_fan(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
return store_bank2_settings(dev, ABIT_UGURU_FAN_LOW_ALARM_ENABLE, buf,
count);
}
/* beeps */
static ssize_t show_beep_bank1(struct device *dev,
struct device_attribute *devattr, char *buf)
{
return show_bank1_settings(dev, devattr, ABIT_UGURU_BEEP_ENABLE, buf);
}
static ssize_t show_beep_bank2(struct device *dev,
struct device_attribute *devattr, char *buf)
{
return show_bank2_settings(dev, ABIT_UGURU_BEEP_ENABLE, buf);
}
static ssize_t store_beep_bank1(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
return store_bank1_settings(dev, devattr, ABIT_UGURU_BEEP_ENABLE, buf,
count);
}
static ssize_t store_beep_bank2(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
return store_bank2_settings(dev, ABIT_UGURU_BEEP_ENABLE, buf, count);
}
/* shutdowns */
static ssize_t show_shutdown_bank1(struct device *dev,
struct device_attribute *devattr, char *buf)
{
return show_bank1_settings(dev, devattr, ABIT_UGURU_SHUTDOWN_ENABLE,
buf);
}
static ssize_t show_shutdown_bank2(struct device *dev,
struct device_attribute *devattr, char *buf)
{
return show_bank2_settings(dev, ABIT_UGURU_SHUTDOWN_ENABLE, buf);
}
static ssize_t store_shutdown_bank1(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
return store_bank1_settings(dev, devattr, ABIT_UGURU_SHUTDOWN_ENABLE,
buf, count);
}
static ssize_t store_shutdown_bank2(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
return store_bank2_settings(dev,ABIT_UGURU_SHUTDOWN_ENABLE,buf,count);
}
/* Fan PWM (speed control) */
static ssize_t show_fan_pwm_reg(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct abituguru_data *data = abituguru_update_device(dev);
ssize_t ret = -EIO;
if (data->valid)
ret = sprintf(buf, "%d\n",
(int)data->fan_pwm_regs[attr->index]);
up(&data->update_lock);
return ret;
}
static ssize_t store_fan_pwm_reg(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct abituguru_data *data = abituguru_update_device(dev);
int val = simple_strtoul(buf, NULL, 10);
ssize_t ret = -EIO;
if (data->valid && (data->fan_pwm_regs[attr->index] != val)) {
u8 buf[5];
memcpy (buf, &data->fan_pwm_regs[(attr->index/5)*5], 5);
buf[attr->index%5] = val;
if (abituguru_write(&data->client,
ABIT_UGURU_FAN_PWM+1, attr->index/5, buf, 5) = 5)
ret = count;
data->valid = 0;
}
up(&data->update_lock);
return ret;
}
static ssize_t show_fan_pwm_sensor(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct abituguru_data *data = abituguru_update_device(dev);
ssize_t ret = -EIO;
if (data->valid) {
/* We need to walk to the temp sensor addresses to find what
the userspace id of the configured temp sensor is. */
int i;
for (i=0; i<data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]; i++) {
if (data->bank1_address[ABIT_UGURU_TEMP_SENSOR][i] =
(data->fan_pwm_regs[attr->index] & 0x0F)) {
ret = sprintf(buf, "%d\n", i+1);
break;
}
}
}
up(&data->update_lock);
return ret;
}
static ssize_t store_fan_pwm_sensor(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct abituguru_data *data = abituguru_update_device(dev);
unsigned long val = simple_strtoul(buf, NULL, 10) - 1;
ssize_t ret = count;
if (!data->valid)
ret = -EIO;
else if (val < data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]) {
u8 buf[5];
u8 address = data->bank1_address[ABIT_UGURU_TEMP_SENSOR][val];
memcpy (buf, &data->fan_pwm_regs[attr->index], 5);
buf[0] = (buf[0]&0xF0) | address;
if (buf[0] != data->fan_pwm_regs[attr->index]) {
if (abituguru_write(&data->client, ABIT_UGURU_FAN_PWM+1,
attr->index/5, buf, 5) != 5)
ret = -EIO;
data->valid = 0;
}
}
up(&data->update_lock);
return ret;
}
static ssize_t show_enable_fan_pwm(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct abituguru_data *data = abituguru_update_device(dev);
ssize_t ret = -EIO;
if (data->valid) {
int i, mask = 0;
for (i=0; i<3; i++) {
if (data->fan_pwm_regs[i*5]&ABIT_UGURU_FAN_PWM_ENABLE)
mask |= 0x01 << i;
}
ret = sprintf(buf, "%d\n", mask);
}
up(&data->update_lock);
return ret;
}
static ssize_t store_enable_fan_pwm(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
struct abituguru_data *data = abituguru_update_device(dev);
int i, mask = simple_strtoul(buf, NULL, 10);
ssize_t ret = -EIO;
if (data->valid) {
ret = count;
for (i=0; i<3; i++) {
u8 buf[5], dirty = 0;
memcpy (buf, &data->fan_pwm_regs[i*5], 5);
/* check if we need to enable */
if ( (mask&(0x01<<i)) &&
!(buf[0]&ABIT_UGURU_FAN_PWM_ENABLE)) {
buf[0] |= ABIT_UGURU_FAN_PWM_ENABLE;
dirty = 1;
}
/* check if we need to disable */
if (!(mask&(0x01<<i)) &&
(buf[0]&ABIT_UGURU_FAN_PWM_ENABLE)) {
buf[0] &= ~ABIT_UGURU_FAN_PWM_ENABLE;
dirty = 1;
}
if (dirty) {
if (abituguru_write(&data->client,
ABIT_UGURU_FAN_PWM+1, i,
buf, 5) != 5)
ret = -EIO;
data->valid = 0;
}
}
}
up(&data->update_lock);
return ret;
}
/* Sysfs attr, the entries for the temp and in sensors are missing, these are
generated automaticly, because we don't know how many temp and in sensors
there are in bank1, luckily we can probe the uguru to find out if a sensor
is not connected, temp or in. */
static struct sensor_device_attribute abituguru_sysfs_attr[] = {
__SENSOR_DEVICE_ATTR(fan1_input, 0444, show_bank2_reg, NULL, 2),
__SENSOR_DEVICE_ATTR(fan1_min, 0644, show_bank2_reg,
store_bank2_reg, 1),
__SENSOR_DEVICE_ATTR(fan1_temp_sensor, 0644, show_fan_pwm_sensor,
store_fan_pwm_sensor, 0),
__SENSOR_DEVICE_ATTR(fan1_pwm_min, 0644, show_fan_pwm_reg,
store_fan_pwm_reg, 1),
__SENSOR_DEVICE_ATTR(fan1_pwm_max, 0644, show_fan_pwm_reg,
store_fan_pwm_reg, 2),
__SENSOR_DEVICE_ATTR(fan1_temp_min, 0644, show_fan_pwm_reg,
store_fan_pwm_reg, 3),
__SENSOR_DEVICE_ATTR(fan1_temp_max, 0644, show_fan_pwm_reg,
store_fan_pwm_reg, 4),
__SENSOR_DEVICE_ATTR(fan2_input, 0444, show_bank2_reg, NULL, 5),
__SENSOR_DEVICE_ATTR(fan2_min, 0644, show_bank2_reg,
store_bank2_reg, 4),
__SENSOR_DEVICE_ATTR(fan2_temp_sensor, 0644, show_fan_pwm_sensor,
store_fan_pwm_sensor, 5),
__SENSOR_DEVICE_ATTR(fan2_pwm_min, 0644, show_fan_pwm_reg,
store_fan_pwm_reg, 6),
__SENSOR_DEVICE_ATTR(fan2_pwm_max, 0644, show_fan_pwm_reg,
store_fan_pwm_reg, 7),
__SENSOR_DEVICE_ATTR(fan2_temp_min, 0644, show_fan_pwm_reg,
store_fan_pwm_reg, 8),
__SENSOR_DEVICE_ATTR(fan2_temp_max, 0644, show_fan_pwm_reg,
store_fan_pwm_reg, 9),
__SENSOR_DEVICE_ATTR(fan3_input, 0444, show_bank2_reg, NULL, 8),
__SENSOR_DEVICE_ATTR(fan3_min, 0644, show_bank2_reg,
store_bank2_reg, 7),
__SENSOR_DEVICE_ATTR(fan3_temp_sensor, 0644, show_fan_pwm_sensor,
store_fan_pwm_sensor, 10),
__SENSOR_DEVICE_ATTR(fan3_pwm_min, 0644, show_fan_pwm_reg,
store_fan_pwm_reg, 11),
__SENSOR_DEVICE_ATTR(fan3_pwm_max, 0644, show_fan_pwm_reg,
store_fan_pwm_reg, 12),
__SENSOR_DEVICE_ATTR(fan3_temp_min, 0644, show_fan_pwm_reg,
store_fan_pwm_reg, 13),
__SENSOR_DEVICE_ATTR(fan3_temp_max, 0644, show_fan_pwm_reg,
store_fan_pwm_reg, 14),
__SENSOR_DEVICE_ATTR(fan4_input, 0444, show_bank2_reg, NULL, 11),
__SENSOR_DEVICE_ATTR(fan4_min, 0644, show_bank2_reg,
store_bank2_reg, 10),
__SENSOR_DEVICE_ATTR(fan5_input, 0444, show_bank2_reg, NULL, 14),
__SENSOR_DEVICE_ATTR(fan5_min, 0644, show_bank2_reg,
store_bank2_reg, 13),
__SENSOR_DEVICE_ATTR(fan6_input, 0444, show_bank2_reg, NULL, 17),
__SENSOR_DEVICE_ATTR(fan6_min, 0644, show_bank2_reg,
store_bank2_reg, 16),
__SENSOR_DEVICE_ATTR(alarm_in, 0444, show_alarm_bank1, NULL,0),
__SENSOR_DEVICE_ATTR(alarm_temp, 0444, show_alarm_bank1, NULL,1),
__SENSOR_DEVICE_ATTR(alarm_fan, 0444, show_alarm_bank2, NULL,0),
__SENSOR_DEVICE_ATTR(enable_in, 0644, show_enable_in,
store_enable_in, 0),
__SENSOR_DEVICE_ATTR(enable_temp, 0644, show_enable_temp,
store_enable_temp, 1),
__SENSOR_DEVICE_ATTR(enable_fan, 0644, show_enable_fan,
store_enable_fan, 0),
__SENSOR_DEVICE_ATTR(enable_fan_pwm, 0644, show_enable_fan_pwm,
store_enable_fan_pwm, 0),
__SENSOR_DEVICE_ATTR(beep_in, 0644, show_beep_bank1,
store_beep_bank1, 0),
__SENSOR_DEVICE_ATTR(beep_temp, 0644, show_beep_bank1,
store_beep_bank1, 1),
__SENSOR_DEVICE_ATTR(beep_fan, 0644, show_beep_bank2,
store_beep_bank2, 0),
__SENSOR_DEVICE_ATTR(shutdown_in, 0644, show_shutdown_bank1,
store_shutdown_bank1, 0),
__SENSOR_DEVICE_ATTR(shutdown_temp, 0644, show_shutdown_bank1,
store_shutdown_bank1, 1),
__SENSOR_DEVICE_ATTR(shutdown_fan, 0644, show_shutdown_bank2,
store_shutdown_bank2, 0)
};
/* Const names for use in the dynamicly genererated in and temp sysfs attr,
16 is a bit over kill since there are know known mainboards with 16 in
or temp sensors, but better safe then sorry. */
static const char * const abituguru_bank1_name[2][3*16] = { {
"in0_input", "in0_min", "in0_max",
"in1_input", "in1_min", "in1_max",
"in2_input", "in2_min", "in2_max",
"in3_input", "in3_min", "in3_max",
"in4_input", "in4_min", "in4_max",
"in5_input", "in5_min", "in5_max",
"in6_input", "in6_min", "in6_max",
"in7_input", "in7_min", "in7_max",
"in8_input", "in8_min", "in8_max",
"in9_input", "in9_min", "in9_max",
"in10_input", "in10_min", "in10_max",
"in11_input", "in11_min", "in11_max",
"in12_input", "in12_min", "in12_max",
"in13_input", "in13_min", "in13_max",
"in14_input", "in14_min", "in14_max",
"in15_input", "in15_min", "in15_max" }, {
"temp1_input", "temp1_max", "temp1_max_hyst",
"temp2_input", "temp2_max", "temp2_max_hyst",
"temp3_input", "temp3_max", "temp3_max_hyst",
"temp4_input", "temp4_max", "temp4_max_hyst",
"temp5_input", "temp5_max", "temp5_max_hyst",
"temp6_input", "temp6_max", "temp6_max_hyst",
"temp7_input", "temp7_max", "temp7_max_hyst",
"temp8_input", "temp8_max", "temp8_max_hyst",
"temp9_input", "temp9_max", "temp9_max_hyst",
"temp10_input", "temp10_max", "temp10_max_hyst",
"temp11_input", "temp11_max", "temp11_max_hyst",
"temp12_input", "temp12_max", "temp12_max_hyst",
"temp13_input", "temp13_max", "temp13_max_hyst",
"temp14_input", "temp14_max", "temp14_max_hyst",
"temp15_input", "temp15_max", "temp15_max_hyst",
"temp16_input", "temp16_max", "temp16_max_hyst" }
};
/* 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 abituguru_driver = {
.owner = THIS_MODULE,
.name = "abituguru",
.id = 0, /* This is optional */
.flags = I2C_DF_NOTIFY,
.attach_adapter = abituguru_detect,
.detach_client = abituguru_detach_client,
};
/* This is called when the module is loaded */
static int abituguru_detect(struct i2c_adapter *adapter)
{
struct i2c_client *new_client;
struct abituguru_data *data;
int i,res,err = 0;
const char client_name[] = "abituguru";
/* El weirdo probe order, to keep the sysfs order identical to the
BIOS and window-appliction listing order. */
const u8 probe_order[16] = { 0x00,0x01,0x03,0x04,0x0A,0x08,0x0E,0x02,
0x09,0x06,0x05,0x0B,0x0F,0x0D,0x07,0x0C };
u8 data_val;
/* Make sure we are probing the ISA bus!! */
if (!i2c_is_isa_adapter(adapter)) {
dev_err(&adapter->dev,
"abituguru_detect called for an I2C bus adapter?!?\n");
err = -ENODEV;
goto ERROR0;
}
/* Reserve our 2 ISA addresses */
if (!request_region(ABIT_UGURU_BASE+ABIT_UGURU_CMD, 1,
abituguru_driver.name)) {
dev_err(&adapter->dev, "ISA-address %x already in use!\n",
ABIT_UGURU_BASE + ABIT_UGURU_CMD);
err = -EBUSY;
goto ERROR0;
}
if (!request_region(ABIT_UGURU_BASE+ABIT_UGURU_DATA, 1,
abituguru_driver.name)) {
dev_err(&adapter->dev, "ISA-address %x already in use!\n",
ABIT_UGURU_BASE + ABIT_UGURU_DATA);
err = -EBUSY;
goto ERROR1;
}
/* Malloc our data and initialise the static parts. */
if (!(data = kmalloc(sizeof(struct abituguru_data), GFP_KERNEL))) {
err = -ENOMEM;
goto ERROR2;
}
memset(data, 0, sizeof(struct abituguru_data));
new_client = &data->client;
i2c_set_clientdata(new_client, data);
new_client->addr = ABIT_UGURU_BASE;
new_client->adapter = adapter;
new_client->driver = &abituguru_driver;
new_client->flags = 0;
strlcpy(new_client->name, client_name, I2C_NAME_SIZE);
init_MUTEX(&data->update_lock);
/* See if there is an uguru there. After a reboot uGuru will hold 0x00
at DATA and 0xAC at CMD. When this driver has already been loaded
once DATA will hold 0x08 that's why we need to test both. */
data_val = inb_p(ABIT_UGURU_BASE + ABIT_UGURU_DATA);
if ( ((data_val = 0x00) || (data_val = 0x08)) &&
(inb_p(ABIT_UGURU_BASE + ABIT_UGURU_CMD) = 0xAC)) {
/* Probably found uGuru, see if its ready */
if (data_val = ABIT_UGURU_STATUS_INPUT)
data->uguru_ready = 1;
} else if (force) {
/* User forced goahead, see if it the uGuru is in waiting
for input state, and ifso force a read to make sure it
is ready for the ready cycle */
printk(KERN_WARNING "No Abit uGuru found, asuming one is present because of \"force\" parameter\n");
if (data_val = ABIT_UGURU_STATUS_INPUT) {
u8 dummy;
data->uguru_ready = 1;
abituguru_read(new_client, ABIT_UGURU_SENSOR_BANK2,
0, &dummy, 1);
}
} else {
/* No uGuru found */
err = -ENODEV;
goto ERROR3;
}
/* Do a test read of the uGuru to see if one really is there,
note that this won't read bank1 since we didn't detect
the bank1 sensors yet. */
abituguru_update_device(&new_client->dev);
if (!data->valid) {
err = -ENODEV;
goto ERROR3;
}
up(&data->update_lock);
data->valid = 0;
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto ERROR3;
/* Register sysfs hooks */
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
goto ERROR4;
}
/* Detect sensor types and register the sysfs hooks for bank1 */
for (i=0; i<16; i++) {
data->bank1_attr[i*3 ].dev_attr.attr.mode = 0444;
data->bank1_attr[i*3 ].dev_attr.attr.owner = THIS_MODULE;
data->bank1_attr[i*3 ].dev_attr.show = show_bank1_reg;
data->bank1_attr[i*3 ].dev_attr.store = NULL;
data->bank1_attr[i*3 ].index = probe_order[i]*4 + 3;
data->bank1_attr[i*3 +1].dev_attr.attr.mode = 0644;
data->bank1_attr[i*3 +1].dev_attr.attr.owner = THIS_MODULE;
data->bank1_attr[i*3 +1].dev_attr.show = show_bank1_reg;
data->bank1_attr[i*3 +1].dev_attr.store = store_bank1_reg;
data->bank1_attr[i*3 +1].index = probe_order[i]*4 + 1;
data->bank1_attr[i*3 +2].dev_attr.attr.mode = 0644;
data->bank1_attr[i*3 +2].dev_attr.attr.owner = THIS_MODULE;
data->bank1_attr[i*3 +2].dev_attr.show = show_bank1_reg;
data->bank1_attr[i*3 +2].dev_attr.store = store_bank1_reg;
data->bank1_attr[i*3 +2].index = probe_order[i]*4 + 2;
res = abituguru_detect_sensor_type(new_client, probe_order[i]);
if (res < 0) {
printk(KERN_WARNING "Couldnot determine sensor type for sensorbank1 sensor %d, skipping sensor\n", i);
continue;
}
if (res != ABIT_UGURU_NC) {
data->bank1_attr[i*3 ].dev_attr.attr.name abituguru_bank1_name[res][
data->bank1_sensors[res]*3 ];
data->bank1_attr[i*3 +1].dev_attr.attr.name abituguru_bank1_name[res][
data->bank1_sensors[res]*3 +1];
data->bank1_attr[i*3 +2].dev_attr.attr.name abituguru_bank1_name[res][
data->bank1_sensors[res]*3 +2];
data->bank1_address[res][data->bank1_sensors[res]] probe_order[i];
data->bank1_sensors[res]++;
device_create_file(&new_client->dev,
&data->bank1_attr[i*3 ].dev_attr);
device_create_file(&new_client->dev,
&data->bank1_attr[i*3 +1].dev_attr);
device_create_file(&new_client->dev,
&data->bank1_attr[i*3 +2].dev_attr);
}
}
/* Fan and misc hooks */
for (i=0; i<40; i++)
device_create_file(&new_client->dev,
&abituguru_sysfs_attr[i].dev_attr);
return 0;
ERROR4:
if (i2c_detach_client(new_client))
dev_err(&new_client->dev,
"Client deregistration failed, client not detached.\n");
ERROR3:
kfree(data);
ERROR2:
release_region(ABIT_UGURU_BASE + ABIT_UGURU_DATA, 1);
ERROR1:
release_region(ABIT_UGURU_BASE + ABIT_UGURU_CMD, 1);
ERROR0:
return err;
}
static int abituguru_detach_client(struct i2c_client *client)
{
int err;
struct abituguru_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->class_dev);
if ((err = i2c_detach_client(client))) {
dev_err(&client->dev,
"Client deregistration failed, client not detached.\n");
return err;
}
release_region(client->addr + ABIT_UGURU_DATA, 1);
release_region(client->addr + ABIT_UGURU_CMD, 1);
kfree(data);
return 0;
}
/* Danger Will Robinson, danger! This function downs the update_lock semaphore
in the abituguru_data struct but doesn't up it, the caller must up
it when it is done with the returned data! */
static struct abituguru_data *abituguru_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct abituguru_data *data = i2c_get_clientdata(client);
int i;
down(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
data->valid = 0;
if (abituguru_read(client, ABIT_UGURU_ALARM_BANK, 0,
data->alarm_regs, 3) != 3)
return data;
for (i = 0; i < 16; i++) {
if (abituguru_read(client, ABIT_UGURU_SENSOR_BANK1,
i, &data->bank1_regs[i*4+3], 1) != 1)
return data;
if (abituguru_read(client, ABIT_UGURU_SENSOR_BANK1+1,
i, &data->bank1_regs[i*4], 3) != 3)
return data;
}
for (i = 0; i < 3; i++) {
if (abituguru_read(client, ABIT_UGURU_FAN_PWM,
i, &data->fan_pwm_regs[i*5], 5) != 5)
return data;
}
for (i = 0; i < 6; i++) {
if (abituguru_read(client, ABIT_UGURU_SENSOR_BANK2,
i, &data->bank2_regs[i*3+2], 1) != 1)
return data;
if (abituguru_read(client, ABIT_UGURU_SENSOR_BANK2+1,
i, &data->bank2_regs[i*3], 2) != 2)
return data;
}
data->last_updated = jiffies;
data->valid = 1;
}
return data;
}
static int __init sm_abituguru_init(void)
{
return i2c_isa_add_driver(&abituguru_driver);
}
static void __exit sm_abituguru_exit(void)
{
i2c_isa_del_driver(&abituguru_driver);
}
MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>");
MODULE_DESCRIPTION("Abit uGuru Sensor device");
MODULE_LICENSE("GPL");
module_init(sm_abituguru_init);
module_exit(sm_abituguru_exit);
-------------- next part --------------
obj-m += abituguru.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
next prev parent reply other threads:[~2005-10-21 20:20 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-10-21 11:15 [lm-sensors] New Abit uGuru driver + libsensors patch, Hans de Goede
2005-10-21 11:16 ` Hans de Goede
2005-10-21 11:17 ` Hans de Goede
2005-10-21 12:26 ` Jean Delvare
2005-10-21 19:41 ` [lm-sensors] New Abit uGuru driver + libsensors patch, review Hans de Goede
2005-10-21 20:01 ` Jean Delvare
2005-10-21 20:20 ` Hans de Goede [this message]
2005-10-22 0:17 ` Grant Coady
2005-10-28 0:27 ` [lm-sensors] New Abit uGuru driver + libsensors patch, Matt Stamp
2005-10-28 0:58 ` Hans de Goede
2005-10-28 5:32 ` Mark M. Hoffman
2005-10-28 6:17 ` Mark M. Hoffman
2005-10-28 11:30 ` [lm-sensors] New Abit uGuru driver + libsensors patch, review Hans de Goede
2005-10-28 20:14 ` Rudolf Marek
2005-10-28 23:08 ` Jean Delvare
2005-10-28 23:13 ` Rudolf Marek
2005-10-29 2:22 ` [lm-sensors] New Abit uGuru driver + libsensors patch, Matthew Stamp
2005-10-29 10:14 ` Hans de Goede
2005-10-29 11:27 ` Matthew Stamp
2005-10-29 12:04 ` [lm-sensors] New Abit uGuru driver + libsensors patch, review Hans de Goede
2005-10-29 19:36 ` [lm-sensors] New Abit uGuru driver + libsensors patch, Matthew Stamp
2005-11-02 0:05 ` [lm-sensors] New Abit uGuru driver + libsensors patch, review Rudolf Marek
2005-11-02 12:22 ` Rudolf Marek
2005-11-02 12:54 ` Hans de Goede
2005-11-02 21:29 ` Jean Delvare
2005-11-02 22:52 ` Hans de Goede
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=43592F7E.300@hhs.nl \
--to=j.w.r.degoede@hhs.nl \
--cc=lm-sensors@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.