mirror of
https://github.com/lm-sensors/lm-sensors.git
synced 2026-10-18 11:09:32 +00:00
git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@4040 7894878c-1315-0410-8ee3-d5d059ff63e0
1533 lines
51 KiB
C
1533 lines
51 KiB
C
/*
|
|
w83792d.c - Part of lm_sensors, Linux kernel modules for hardware
|
|
monitoring
|
|
Copyright (c) 2004, 2005 Winbond Electronics Corp.
|
|
Chunhao Huang
|
|
|
|
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:
|
|
1. This driver is only for 2.4 kernel(2.4.10 or later), 2.6 kernel
|
|
need a different driver.
|
|
2. This driver is only for Winbond W83792D C version device, there
|
|
are also some motherboards with B version W83792D device. The
|
|
calculation method to in6-in7(measured value, limits) is a little
|
|
different between C and B version. C or B version can be identified
|
|
by CR[0x49h].
|
|
3. The function of chassis open detection need further test.
|
|
4. The function of vid and vrm has not been finished, because I'm NOT
|
|
very familiar with them. If someone can finish it, that's good,
|
|
then please delete this note 4.
|
|
*/
|
|
|
|
/*
|
|
Supports following chips:
|
|
|
|
Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA
|
|
w83792d 9 7 3 3 0x7a 0x5ca3 yes no
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/i2c-proc.h>
|
|
#include <linux/init.h>
|
|
#include "version.h"
|
|
#include "sensors_vid.h"
|
|
|
|
/* Addresses to scan */
|
|
static unsigned short normal_i2c[] = { SENSORS_I2C_END };
|
|
static unsigned short normal_i2c_range[] = { 0x2c, 0x2f, SENSORS_I2C_END };
|
|
static unsigned int normal_isa[] = { SENSORS_ISA_END };
|
|
static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
|
|
|
|
/* Insmod parameters */
|
|
SENSORS_INSMOD_1(w83792d);
|
|
SENSORS_MODULE_PARM(force_subclients, "List of subclient addresses: " \
|
|
"{bus, clientaddr, subclientaddr1, subclientaddr2}");
|
|
|
|
static int init;
|
|
MODULE_PARM(init, "i");
|
|
MODULE_PARM_DESC(init, "Set to one for chip initialization");
|
|
|
|
/* Enable/Disable w83792d debugging output */
|
|
/* #define W83792D_DEBUG 1 */
|
|
|
|
/* Constants specified below */
|
|
#define W83792D_REG_GPIO_EN 0x1A
|
|
#define W83792D_REG_CONFIG 0x40
|
|
#define W83792D_REG_I2C_ADDR 0x48
|
|
#define W83792D_REG_CHIPID 0x49 /* contains version ID: A/B/C */
|
|
#define W83792D_REG_I2C_SUBADDR 0x4A
|
|
#define W83792D_REG_PIN 0x4B
|
|
#define W83792D_REG_IRQ 0x4C
|
|
#define W83792D_REG_BANK 0x4E
|
|
#define W83792D_REG_CHIPMAN 0x4F /* contains the vendor ID */
|
|
#define W83792D_REG_WCHIPID 0x58 /* contains the chip ID */
|
|
#define W83792D_REG_VID_IN_B 0x17 /* ctroll in0/in1 's limit modifiability */
|
|
|
|
static const u8 W83792D_REG_IN[9] = {
|
|
0x20, /* Vcore A in DataSheet */
|
|
0x21, /* Vcore B in DataSheet */
|
|
0x22, /* VIN0 in DataSheet */
|
|
0x23, /* VIN1 in DataSheet */
|
|
0x24, /* VIN2 in DataSheet */
|
|
0x25, /* VIN3 in DataSheet */
|
|
0x26, /* 5VCC in DataSheet */
|
|
0xB0, /* 5VSB in DataSheet */
|
|
0xB1 /* VBAT in DataSheet */
|
|
};
|
|
#define W83792D_REG_LOW_BITS1 0x3E /* Low Bits I in DataSheet */
|
|
#define W83792D_REG_LOW_BITS2 0x3F /* Low Bits II in DataSheet */
|
|
static const u8 W83792D_REG_IN_MAX[9] = {
|
|
0x2B, /* Vcore A High Limit in DataSheet */
|
|
0x2D, /* Vcore B High Limit in DataSheet */
|
|
0x2F, /* VIN0 High Limit in DataSheet */
|
|
0x31, /* VIN1 High Limit in DataSheet */
|
|
0x33, /* VIN2 High Limit in DataSheet */
|
|
0x35, /* VIN3 High Limit in DataSheet */
|
|
0x37, /* 5VCC High Limit in DataSheet */
|
|
0xB4, /* 5VSB High Limit in DataSheet */
|
|
0xB6 /* VBAT High Limit in DataSheet */
|
|
};
|
|
static const u8 W83792D_REG_IN_MIN[9] = {
|
|
0x2C, /* Vcore A Low Limit in DataSheet */
|
|
0x2E, /* Vcore B Low Limit in DataSheet */
|
|
0x30, /* VIN0 Low Limit in DataSheet */
|
|
0x32, /* VIN1 Low Limit in DataSheet */
|
|
0x34, /* VIN2 Low Limit in DataSheet */
|
|
0x36, /* VIN3 Low Limit in DataSheet */
|
|
0x38, /* 5VCC Low Limit in DataSheet */
|
|
0xB5, /* 5VSB Low Limit in DataSheet */
|
|
0xB7 /* VBAT Low Limit in DataSheet */
|
|
};
|
|
|
|
static const u8 W83792D_REG_FAN[7] = {
|
|
0x28, /* FAN 1 Count in DataSheet */
|
|
0x29, /* FAN 2 Count in DataSheet */
|
|
0x2A, /* FAN 3 Count in DataSheet */
|
|
0xB8, /* FAN 4 Count in DataSheet */
|
|
0xB9, /* FAN 5 Count in DataSheet */
|
|
0xBA, /* FAN 6 Count in DataSheet */
|
|
0xBE /* FAN 7 Count in DataSheet */
|
|
};
|
|
static const u8 W83792D_REG_FAN_MIN[7] = {
|
|
0x3B, /* FAN 1 Count Low Limit in DataSheet */
|
|
0x3C, /* FAN 2 Count Low Limit in DataSheet */
|
|
0x3D, /* FAN 3 Count Low Limit in DataSheet */
|
|
0xBB, /* FAN 4 Count Low Limit in DataSheet */
|
|
0xBC, /* FAN 5 Count Low Limit in DataSheet */
|
|
0xBD, /* FAN 6 Count Low Limit in DataSheet */
|
|
0xBF /* FAN 7 Count Low Limit in DataSheet */
|
|
};
|
|
#define W83792D_REG_FAN_CFG 0x84 /* FAN Configuration in DataSheet */
|
|
static const u8 W83792D_REG_PWM[7] = {
|
|
0x81, /* FAN 1 Duty Cycle, be used to control */
|
|
0x83, /* FAN 2 Duty Cycle, be used to control */
|
|
0x94, /* FAN 3 Duty Cycle, be used to control */
|
|
0xA3, /* FAN 4 Duty Cycle, be used to control */
|
|
0xA4, /* FAN 5 Duty Cycle, be used to control */
|
|
0xA5, /* FAN 6 Duty Cycle, be used to control */
|
|
0xA6 /* FAN 7 Duty Cycle, be used to control */
|
|
};
|
|
|
|
#define W83792D_REG_TEMP1 0x27 /* TEMP 1 in DataSheet */
|
|
#define W83792D_REG_TEMP1_OVER 0x39 /* TEMP 1 High Limit in DataSheet */
|
|
#define W83792D_REG_TEMP1_HYST 0x3A /* TEMP 1 Low Limit in DataSheet */
|
|
static const u8 W83792D_REG_TEMP_ADD[2][7] = {
|
|
{ 0xC0, /* TEMP 2 in DataSheet */
|
|
0xC1, /* TEMP 2(0.5 deg) in DataSheet */
|
|
0xC5, /* TEMP 2 Over High part in DataSheet */
|
|
0xC6, /* TEMP 2 Over Low part in DataSheet */
|
|
0xC3, /* TEMP 2 Thyst High part in DataSheet */
|
|
0xC4, /* TEMP 2 Thyst Low part in DataSheet */
|
|
0xC2 }, /* TEMP 2 Config in DataSheet */
|
|
{ 0xC8, /* TEMP 3 in DataSheet */
|
|
0xC9, /* TEMP 3(0.5 deg) in DataSheet */
|
|
0xCD, /* TEMP 3 Over High part in DataSheet */
|
|
0xCE, /* TEMP 3 Over Low part in DataSheet */
|
|
0xCB, /* TEMP 3 Thyst High part in DataSheet */
|
|
0xCC, /* TEMP 3 Thyst Low part in DataSheet */
|
|
0xCA } /* TEMP 3 Config in DataSheet */
|
|
};
|
|
|
|
static const u8 W83792D_REG_FAN_DIV[4] = {
|
|
0x47, /* contains FAN2 and FAN1 Divisor */
|
|
0x5B, /* contains FAN4 and FAN3 Divisor */
|
|
0x5C, /* contains FAN6 and FAN5 Divisor */
|
|
0x9E /* contains FAN7 Divisor. */
|
|
};
|
|
|
|
#define W83792D_REG_ALARM1 0xA9 /* realtime status register1 */
|
|
#define W83792D_REG_ALARM2 0xAA /* realtime status register2 */
|
|
#define W83792D_REG_ALARM3 0xAB /* realtime status register3 */
|
|
#define W83792D_REG_CASE_OPEN 0x42 /* Bit 5: Case Open status bit */
|
|
#define W83792D_REG_CASE_OPEN_CLR 0x44 /* Bit 7: Case Open CLR_CHS/Reset bit */
|
|
|
|
static const u8 W83792D_REG_THERMAL[3] = {
|
|
0x85, /* SmartFanI: Fan1 target value */
|
|
0x86, /* SmartFanI: Fan2 target value */
|
|
0x96 /* SmartFanI: Fan3 target value */
|
|
};
|
|
|
|
static const u8 W83792D_REG_FAN_TOL[3] = {
|
|
0x87, /* (bit3-0)SmartFan Fan1 tolerance */
|
|
0x87, /* (bit7-4)SmartFan Fan2 tolerance */
|
|
0x97 /* (bit3-0)SmartFan Fan3 tolerance */
|
|
};
|
|
|
|
static const u8 W83792D_REG_POINTS[3][4] = {
|
|
{ 0x85, /* SmartFanII: Fan1 temp point 1 */
|
|
0xE3, /* SmartFanII: Fan1 temp point 2 */
|
|
0xE4, /* SmartFanII: Fan1 temp point 3 */
|
|
0xE5 }, /* SmartFanII: Fan1 temp point 4 */
|
|
{ 0x86, /* SmartFanII: Fan2 temp point 1 */
|
|
0xE6, /* SmartFanII: Fan2 temp point 2 */
|
|
0xE7, /* SmartFanII: Fan2 temp point 3 */
|
|
0xE8 }, /* SmartFanII: Fan2 temp point 4 */
|
|
{ 0x96, /* SmartFanII: Fan3 temp point 1 */
|
|
0xE9, /* SmartFanII: Fan3 temp point 2 */
|
|
0xEA, /* SmartFanII: Fan3 temp point 3 */
|
|
0xEB } /* SmartFanII: Fan3 temp point 4 */
|
|
};
|
|
|
|
static const u8 W83792D_REG_LEVELS[3][4] = {
|
|
{ 0x88, /* (bit3-0) SmartFanII: Fan1 Non-Stop */
|
|
0x88, /* (bit7-4) SmartFanII: Fan1 Level 1 */
|
|
0xE0, /* (bit7-4) SmartFanII: Fan1 Level 2 */
|
|
0xE0 }, /* (bit3-0) SmartFanII: Fan1 Level 3 */
|
|
{ 0x89, /* (bit3-0) SmartFanII: Fan2 Non-Stop */
|
|
0x89, /* (bit7-4) SmartFanII: Fan2 Level 1 */
|
|
0xE1, /* (bit7-4) SmartFanII: Fan2 Level 2 */
|
|
0xE1 }, /* (bit3-0) SmartFanII: Fan2 Level 3 */
|
|
{ 0x98, /* (bit3-0) SmartFanII: Fan3 Non-Stop */
|
|
0x98, /* (bit7-4) SmartFanII: Fan3 Level 1 */
|
|
0xE2, /* (bit7-4) SmartFanII: Fan3 Level 2 */
|
|
0xE2 } /* (bit3-0) SmartFanII: Fan3 Level 3 */
|
|
};
|
|
|
|
static inline u8 FAN_TO_REG(long rpm, int div)
|
|
{
|
|
if (rpm == 0)
|
|
return 255;
|
|
rpm = SENSORS_LIMIT(rpm, 1, 1000000);
|
|
return SENSORS_LIMIT(1350000/(rpm * div), 1, 254);
|
|
}
|
|
|
|
#define IN_FROM_REG(nr,val) (((nr)<=1)?(val*2): \
|
|
((((nr)==6)||((nr)==7))?(val*6):(val*4)))
|
|
#define IN_TO_REG(nr,val) (((nr)<=1)?(val/2): \
|
|
((((nr)==6)||((nr)==7))?(val/6):(val/4)))
|
|
#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10)
|
|
#define TEMP_TO_REG(val) (SENSORS_LIMIT((val>=0)?((val)/10):((val)/10+256), 0, 255))
|
|
#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))
|
|
#define DIV_FROM_REG(val) (1 << (val))
|
|
|
|
#ifdef W83792D_DEBUG
|
|
#define ENTER() printk(KERN_DEBUG "w83792d: ENTERING %s, line: %d\n", __FUNCTION__, __LINE__);
|
|
#define LEAVE() printk(KERN_DEBUG "w83792d: LEAVING %s, line: %d\n", __FUNCTION__, __LINE__);
|
|
#else
|
|
#define ENTER()
|
|
#define LEAVE()
|
|
#endif
|
|
|
|
struct w83792d_data {
|
|
struct i2c_client client;
|
|
int sysctl_id;
|
|
enum chips type;
|
|
|
|
struct semaphore update_lock;
|
|
char valid; /* !=0 if following fields are valid */
|
|
unsigned long last_updated; /* In jiffies */
|
|
|
|
struct i2c_client *lm75[2]; /* for secondary I2C addresses */
|
|
|
|
u8 in[9]; /* Register value */
|
|
u8 in_max[9]; /* Register value */
|
|
u8 in_min[9]; /* Register value */
|
|
u16 low_bits; /* Register value */
|
|
u8 has_fan; /* Bit vector */
|
|
u8 fan[7]; /* Register value */
|
|
u8 fan_min[7]; /* Register value */
|
|
u8 fan_cfg; /* Configure Fan Mode */
|
|
u8 temp1[3]; /* Register value */
|
|
u8 temp_add[2][7]; /* Register value */
|
|
u8 fan_div[7]; /* Fan Divisor */
|
|
/*u8 vid; Register encoding, combined */
|
|
u8 pwm[7]; /* We only consider the first 3 set of pwm,
|
|
although 792 chip has 7 set of pwm. */
|
|
u8 pwm_flag[7]; /* indicates PWM or DC mode: 1->PWM; 0->DC */
|
|
/* u8 vrm; VRM version */
|
|
u32 alarms; /* realtime status register encoding,combined */
|
|
u8 chassis[2]; /* [0]->Chassis status, [1]->CLR_CHS */
|
|
u8 thermal_cruise[3]; /* Smart FanI: Fan1,2,3 target value */
|
|
u8 fan_tolerance[3]; /* Fan1,2,3 tolerance(Smart Fan I/II) */
|
|
u8 sf2_points[3][4]; /* Smart FanII: Fan1,2,3 temperature points */
|
|
u8 sf2_levels[3][4]; /* Smart FanII: Fan1,2,3 duty cycle levels */
|
|
};
|
|
|
|
/* Read the w83792d register value, only use bank 0 of the 792 chip */
|
|
static inline int
|
|
w83792d_read_value(struct i2c_client *client, u8 reg)
|
|
{
|
|
return i2c_smbus_read_byte_data(client, reg);
|
|
}
|
|
|
|
/* Write value into the w83792d registers, only use bank 0 of the 792 chip */
|
|
static inline int
|
|
w83792d_write_value(struct i2c_client *client, u8 reg, u8 value)
|
|
{
|
|
return i2c_smbus_write_byte_data(client, reg, value);
|
|
}
|
|
|
|
static int w83792d_attach_adapter(struct i2c_adapter *adapter);
|
|
static int w83792d_detect(struct i2c_adapter *adapter, int address,
|
|
unsigned short flags, int kind);
|
|
static int w83792d_detach_client(struct i2c_client *client);
|
|
|
|
static void w83792d_init_client(struct i2c_client *client);
|
|
static void w83792d_update_client(struct i2c_client *client);
|
|
#ifdef W83792D_DEBUG
|
|
static void w83792d_print_debug(struct w83792d_data *data);
|
|
#endif
|
|
static void w83792d_in(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results);
|
|
static void w83792d_fan(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results);
|
|
static void w83792d_temp(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results);
|
|
static void w83792d_temp_add(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results);
|
|
/*static void w83792d_vid(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results);
|
|
static void w83792d_vrm(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results); */
|
|
static void w83792d_set_fan_div(struct i2c_client *client,
|
|
int nr, u8 newdiv);
|
|
static void w83792d_fan_div(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results);
|
|
static void w83792d_alarms(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results);
|
|
static void w83792d_chassis(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results);
|
|
static void w83792d_pwm(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results);
|
|
static void w83792d_pwm_flag(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results);
|
|
static void w83792d_fan_cfg(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results);
|
|
static void w83792d_thermal_cruise(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results);
|
|
static void w83792d_fan_tolerance(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results);
|
|
static void w83792d_sf2_points(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results);
|
|
static void w83792d_sf2_levels(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results);
|
|
|
|
static struct i2c_driver w83792d_driver = {
|
|
.name = "W83792D sensor driver",
|
|
.flags = I2C_DF_NOTIFY,
|
|
.attach_adapter = w83792d_attach_adapter,
|
|
.detach_client = w83792d_detach_client,
|
|
};
|
|
|
|
/* The /proc/sys entries */
|
|
/* -- SENSORS SYSCTL START -- */
|
|
|
|
#define W83792D_SYSCTL_IN0 1000
|
|
#define W83792D_SYSCTL_IN1 1001
|
|
#define W83792D_SYSCTL_IN2 1002
|
|
#define W83792D_SYSCTL_IN3 1003
|
|
#define W83792D_SYSCTL_IN4 1004
|
|
#define W83792D_SYSCTL_IN5 1005
|
|
#define W83792D_SYSCTL_IN6 1006
|
|
#define W83792D_SYSCTL_IN7 1007
|
|
#define W83792D_SYSCTL_IN8 1008
|
|
#define W83792D_SYSCTL_FAN1 1101
|
|
#define W83792D_SYSCTL_FAN2 1102
|
|
#define W83792D_SYSCTL_FAN3 1103
|
|
#define W83792D_SYSCTL_FAN4 1104
|
|
#define W83792D_SYSCTL_FAN5 1105
|
|
#define W83792D_SYSCTL_FAN6 1106
|
|
#define W83792D_SYSCTL_FAN7 1107
|
|
|
|
#define W83792D_SYSCTL_TEMP1 1200
|
|
#define W83792D_SYSCTL_TEMP2 1201
|
|
#define W83792D_SYSCTL_TEMP3 1202
|
|
/*#define W83792D_SYSCTL_VID 1300
|
|
#define W83792D_SYSCTL_VRM 1301*/
|
|
#define W83792D_SYSCTL_PWM_FLAG 1400
|
|
#define W83792D_SYSCTL_PWM1 1401
|
|
#define W83792D_SYSCTL_PWM2 1402
|
|
#define W83792D_SYSCTL_PWM3 1403
|
|
#define W83792D_SYSCTL_FAN_CFG 1500 /* control Fan Mode */
|
|
#define W83792D_SYSCTL_FAN_DIV 1501
|
|
#define W83792D_SYSCTL_CHASSIS 1502 /* control Case Open */
|
|
#define W83792D_SYSCTL_ALARMS 1503
|
|
|
|
#define W83792D_SYSCTL_THERMAL_CRUISE 1600 /* Smart Fan I: target value */
|
|
#define W83792D_SYSCTL_FAN_TOLERANCE 1601 /* Smart Fan I/II: tolerance */
|
|
#define W83792D_SYSCTL_SF2_POINTS_FAN1 1602 /* Smart Fan II: Fan1 points */
|
|
#define W83792D_SYSCTL_SF2_POINTS_FAN2 1603 /* Smart Fan II: Fan2 points */
|
|
#define W83792D_SYSCTL_SF2_POINTS_FAN3 1604 /* Smart Fan II: Fan3 points */
|
|
#define W83792D_SYSCTL_SF2_LEVELS_FAN1 1605 /* Smart Fan II: Fan1 levels */
|
|
#define W83792D_SYSCTL_SF2_LEVELS_FAN2 1606 /* Smart Fan II: Fan2 levels */
|
|
#define W83792D_SYSCTL_SF2_LEVELS_FAN3 1607 /* Smart Fan II: Fan3 levels */
|
|
|
|
#define W83792D_ALARM_IN0 0x0001
|
|
#define W83792D_ALARM_IN1 0x0002
|
|
#define W83792D_ALARM_IN2 0x0100
|
|
#define W83792D_ALARM_IN3 0x0200
|
|
#define W83792D_ALARM_IN4 0x0400
|
|
#define W83792D_ALARM_IN5 0x0800
|
|
#define W83792D_ALARM_IN6 0x1000
|
|
#define W83792D_ALARM_IN7 0x80000
|
|
#define W83792D_ALARM_IN8 0x100000
|
|
#define W83792D_ALARM_TEMP1 0x0004
|
|
#define W83792D_ALARM_TEMP2 0x0008
|
|
#define W83792D_ALARM_TEMP3 0x0010
|
|
#define W83792D_ALARM_FAN1 0x0020
|
|
#define W83792D_ALARM_FAN2 0x0040
|
|
#define W83792D_ALARM_FAN3 0x0080
|
|
#define W83792D_ALARM_FAN4 0x200000
|
|
#define W83792D_ALARM_FAN5 0x400000
|
|
#define W83792D_ALARM_FAN6 0x800000
|
|
#define W83792D_ALARM_FAN7 0x8000
|
|
|
|
/* -- SENSORS SYSCTL END -- */
|
|
|
|
/* These files are created for detected chip,
|
|
W83792D has 9 voltages 7 fans and 3 temperatures. */
|
|
static ctl_table w83792d_dir_table_template[] =
|
|
{
|
|
{W83792D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_in},
|
|
{W83792D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_in},
|
|
{W83792D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_in},
|
|
{W83792D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_in},
|
|
{W83792D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_in},
|
|
{W83792D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_in},
|
|
{W83792D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_in},
|
|
{W83792D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_in},
|
|
{W83792D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_in},
|
|
{W83792D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_fan},
|
|
{W83792D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_fan},
|
|
{W83792D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_fan},
|
|
{W83792D_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_fan},
|
|
{W83792D_SYSCTL_FAN5, "fan5", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_fan},
|
|
{W83792D_SYSCTL_FAN6, "fan6", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_fan},
|
|
{W83792D_SYSCTL_FAN7, "fan7", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_fan},
|
|
{W83792D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_temp},
|
|
{W83792D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_temp_add},
|
|
{W83792D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_temp_add},
|
|
/*{W83792D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_vid}, */
|
|
{W83792D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0444, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_fan_div},
|
|
{W83792D_SYSCTL_ALARMS, "alarms", NULL, 0, 0644, NULL,
|
|
&i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_alarms},
|
|
{W83792D_SYSCTL_CHASSIS, "chassis", NULL, 0, 0644, NULL,
|
|
&i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_chassis},
|
|
{W83792D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_pwm},
|
|
{W83792D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_pwm},
|
|
{W83792D_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_pwm},
|
|
{W83792D_SYSCTL_PWM_FLAG, "pwm_flag", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_pwm_flag},
|
|
{W83792D_SYSCTL_FAN_CFG, "fan_cfg", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_fan_cfg},
|
|
/*{W83792D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real,
|
|
&i2c_sysctl_real, NULL, &w83792d_vrm}, */
|
|
{W83792D_SYSCTL_THERMAL_CRUISE, "thermal_cruise", NULL, 0, 0644, NULL,
|
|
&i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_thermal_cruise},
|
|
{W83792D_SYSCTL_FAN_TOLERANCE, "fan_tolerance", NULL, 0, 0644,
|
|
NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_fan_tolerance},
|
|
{W83792D_SYSCTL_SF2_POINTS_FAN1, "sf2_points_fan1", NULL, 0, 0644, NULL,
|
|
&i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_sf2_points},
|
|
{W83792D_SYSCTL_SF2_POINTS_FAN2, "sf2_points_fan2", NULL, 0, 0644, NULL,
|
|
&i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_sf2_points},
|
|
{W83792D_SYSCTL_SF2_POINTS_FAN3, "sf2_points_fan3", NULL, 0, 0644, NULL,
|
|
&i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_sf2_points},
|
|
{W83792D_SYSCTL_SF2_LEVELS_FAN1, "sf2_levels_fan1", NULL, 0, 0644, NULL,
|
|
&i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_sf2_levels},
|
|
{W83792D_SYSCTL_SF2_LEVELS_FAN2, "sf2_levels_fan2", NULL, 0, 0644, NULL,
|
|
&i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_sf2_levels},
|
|
{W83792D_SYSCTL_SF2_LEVELS_FAN3, "sf2_levels_fan3", NULL, 0, 0644, NULL,
|
|
&i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_sf2_levels},
|
|
{0}
|
|
};
|
|
|
|
/* This function is called when:
|
|
* w83792d_driver is inserted (when this module is loaded), for each
|
|
available adapter
|
|
* when a new adapter is inserted (and w83792d_driver is still present) */
|
|
static int w83792d_attach_adapter(struct i2c_adapter *adapter)
|
|
{
|
|
int i_tmp;
|
|
ENTER()
|
|
i_tmp = i2c_detect(adapter, &addr_data, w83792d_detect);
|
|
LEAVE()
|
|
return i_tmp;
|
|
}
|
|
|
|
static int
|
|
w83792d_create_subclient(struct i2c_adapter *adapter,
|
|
struct i2c_client *new_client, int addr,
|
|
struct i2c_client **sub_cli)
|
|
{
|
|
int err;
|
|
struct i2c_client *sub_client;
|
|
|
|
(*sub_cli) = sub_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
|
|
if (!(sub_client)) {
|
|
return -ENOMEM;
|
|
}
|
|
memset(sub_client, 0x00, sizeof(struct i2c_client));
|
|
sub_client->addr = 0x48 + addr;
|
|
sub_client->data = NULL;
|
|
sub_client->adapter = adapter;
|
|
sub_client->driver = &w83792d_driver;
|
|
sub_client->flags = 0;
|
|
strcpy(sub_client->name, "w83792d subclient");
|
|
if ((err = i2c_attach_client(sub_client))) {
|
|
printk(KERN_ERR "w83792: subclient registration "
|
|
"at address 0x%x failed\n", sub_client->addr);
|
|
kfree(sub_client);
|
|
return err;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
w83792d_detect_subclients(struct i2c_adapter *adapter, int address, int kind,
|
|
struct i2c_client *new_client)
|
|
{
|
|
int i, id, err;
|
|
u8 val;
|
|
struct w83792d_data *data = new_client->data;
|
|
|
|
data->lm75[0] = NULL;
|
|
data->lm75[1] = NULL;
|
|
|
|
id = i2c_adapter_id(adapter);
|
|
if (force_subclients[0] == id && force_subclients[1] == address) {
|
|
for (i = 2; i <= 3; i++) {
|
|
if (force_subclients[i] < 0x48 ||
|
|
force_subclients[i] > 0x4f) {
|
|
printk(KERN_ERR "w83792d: invalid subclient "
|
|
"address %d; must be 0x48-0x4f\n",
|
|
force_subclients[i]);
|
|
err = -ENODEV;
|
|
goto ERROR_SC_0;
|
|
}
|
|
}
|
|
w83792d_write_value(new_client, W83792D_REG_I2C_SUBADDR,
|
|
(force_subclients[2] & 0x07) |
|
|
((force_subclients[3] & 0x07) << 4));
|
|
}
|
|
|
|
val = w83792d_read_value(new_client, W83792D_REG_I2C_SUBADDR);
|
|
if (!(val & 0x08)) {
|
|
err = w83792d_create_subclient(adapter, new_client, val & 0x7,
|
|
&data->lm75[0]);
|
|
if (err < 0)
|
|
goto ERROR_SC_0;
|
|
}
|
|
if (!(val & 0x80)) {
|
|
if ((data->lm75[0] != NULL) &&
|
|
((val & 0x7) == ((val >> 4) & 0x7))) {
|
|
printk(KERN_ERR "w83792d: duplicate addresses 0x%x, "
|
|
"use force_subclient\n", data->lm75[0]->addr);
|
|
err = -ENODEV;
|
|
goto ERROR_SC_1;
|
|
}
|
|
err = w83792d_create_subclient(adapter, new_client,
|
|
(val >> 4) & 0x7, &data->lm75[1]);
|
|
if (err < 0)
|
|
goto ERROR_SC_1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
/* Undo inits in case of errors */
|
|
|
|
ERROR_SC_1:
|
|
if (data->lm75[0] != NULL) {
|
|
i2c_detach_client(data->lm75[0]);
|
|
kfree(data->lm75[0]);
|
|
}
|
|
ERROR_SC_0:
|
|
return err;
|
|
}
|
|
|
|
|
|
static int w83792d_detect(struct i2c_adapter *adapter, int address,
|
|
unsigned short flags, int kind)
|
|
{
|
|
int i, val1 = 0, val2 = 0;
|
|
struct i2c_client *new_client;
|
|
struct w83792d_data *data;
|
|
int err = 0;
|
|
const char *type_name = "";
|
|
const char *client_name = "";
|
|
|
|
ENTER()
|
|
|
|
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
|
|
LEAVE()
|
|
goto ERROR0;
|
|
}
|
|
|
|
/* OK. For now, we presume we have a valid client. We now create the
|
|
client structure, even though we cannot fill it completely yet.
|
|
But it allows us to access w83792d_{read,write}_value. */
|
|
|
|
if (!(data = kmalloc(sizeof(struct w83792d_data), GFP_KERNEL))) {
|
|
printk(KERN_ERR "w83792d: Out of memory in w83792d_detect (new_client).\n");
|
|
err = -ENOMEM;
|
|
LEAVE()
|
|
goto ERROR0;
|
|
}
|
|
|
|
new_client = &data->client;
|
|
new_client->addr = address;
|
|
new_client->data = data;
|
|
new_client->adapter = adapter;
|
|
new_client->driver = &w83792d_driver;
|
|
new_client->flags = 0;
|
|
|
|
/* Now, we do the remaining detection. */
|
|
if (kind < 0) {
|
|
if (w83792d_read_value(new_client, W83792D_REG_CONFIG)&0x80) {
|
|
LEAVE()
|
|
goto ERROR1;
|
|
}
|
|
val1 = w83792d_read_value(new_client, W83792D_REG_BANK);
|
|
val2 = w83792d_read_value(new_client, W83792D_REG_CHIPMAN);
|
|
#ifdef W83792D_DEBUG
|
|
printk(KERN_DEBUG "w83792d: val1 is: %d, val2 is: %d\n",
|
|
val1, val2);
|
|
#endif
|
|
/* Check for Winbond ID if in bank 0 */
|
|
if (!(val1 & 0x07)) { /* is Bank0 */
|
|
if (((!(val1 & 0x80)) && (val2 != 0xa3)) ||
|
|
((val1 & 0x80) && (val2 != 0x5c))) {
|
|
LEAVE()
|
|
goto ERROR1;
|
|
}
|
|
}
|
|
/* check address at 0x48. */
|
|
if (w83792d_read_value(new_client, W83792D_REG_I2C_ADDR)
|
|
!= address) {
|
|
LEAVE()
|
|
goto ERROR1;
|
|
}
|
|
}
|
|
|
|
/* We have either had a force parameter, or we have already detected
|
|
the Winbond. Put it now into bank 0 and Vendor ID High Byte */
|
|
w83792d_write_value(new_client, W83792D_REG_BANK,
|
|
(w83792d_read_value(new_client,
|
|
W83792D_REG_BANK) & 0x78) |
|
|
0x80);
|
|
|
|
/* Determine the chip type. */
|
|
if (kind <= 0) {
|
|
/* get vendor ID */
|
|
val2 = w83792d_read_value(new_client, W83792D_REG_CHIPMAN);
|
|
if (val2 != 0x5c) { /* the vendor is NOT Winbond */
|
|
LEAVE()
|
|
goto ERROR1;
|
|
}
|
|
val1 = w83792d_read_value(new_client, W83792D_REG_WCHIPID);
|
|
if (val1 == 0x7a) {
|
|
kind = w83792d;
|
|
} else {
|
|
if (kind == 0)
|
|
printk(KERN_WARNING "w83792d: Ignoring "
|
|
"'force' parameter for unknown chip "
|
|
"at adapter %d, address 0x%02x\n",
|
|
i2c_adapter_id(adapter), address);
|
|
LEAVE()
|
|
goto ERROR1;
|
|
}
|
|
}
|
|
|
|
if (kind == w83792d) {
|
|
type_name = "w83792d";
|
|
client_name = "W83792D chip";
|
|
} else {
|
|
printk(KERN_ERR "w83792d: Internal error: unknown kind (%d)?!?",
|
|
kind);
|
|
LEAVE()
|
|
goto ERROR1;
|
|
}
|
|
|
|
/* Fill in the remaining client fields and put it into the global list */
|
|
strcpy(new_client->name, client_name);
|
|
data->type = kind;
|
|
data->valid = 0;
|
|
data->has_fan = 0x07; /* at least 3 fan inputs */
|
|
init_MUTEX(&data->update_lock);
|
|
|
|
/* Tell the I2C layer a new client has arrived */
|
|
if ((err = i2c_attach_client(new_client))) {
|
|
LEAVE()
|
|
goto ERROR1;
|
|
}
|
|
|
|
if ((err = w83792d_detect_subclients(adapter, address,
|
|
kind, new_client)))
|
|
goto ERROR2;
|
|
|
|
/* Read GPIO enable register to check if pins for fan 4,5 are used as
|
|
GPIO */
|
|
val1 = w83792d_read_value(new_client, W83792D_REG_GPIO_EN);
|
|
if (!(val1 & 0x40))
|
|
data->has_fan |= 0x08; /* fan 4 */
|
|
if (!(val1 & 0x20))
|
|
data->has_fan |= 0x10; /* fan 5 */
|
|
|
|
val1 = w83792d_read_value(new_client, W83792D_REG_PIN);
|
|
if (val1 & 0x40)
|
|
data->has_fan |= 0x20; /* fan 6 */
|
|
if (val1 & 0x04)
|
|
data->has_fan |= 0x40; /* fan 7 */
|
|
|
|
/* Register a new directory entry with module sensors */
|
|
if ((i = i2c_register_entry(new_client, type_name,
|
|
w83792d_dir_table_template, THIS_MODULE)) < 0) {
|
|
err = i;
|
|
goto ERROR3;
|
|
}
|
|
data->sysctl_id = i;
|
|
|
|
/* Initialize the chip */
|
|
w83792d_init_client(new_client);
|
|
LEAVE()
|
|
return 0;
|
|
|
|
ERROR3:
|
|
if (data->lm75[0] != NULL) {
|
|
i2c_detach_client(data->lm75[0]);
|
|
kfree(data->lm75[0]);
|
|
}
|
|
if (data->lm75[1] != NULL) {
|
|
i2c_detach_client(data->lm75[1]);
|
|
kfree(data->lm75[1]);
|
|
}
|
|
ERROR2:
|
|
i2c_detach_client(new_client);
|
|
ERROR1:
|
|
kfree(data);
|
|
ERROR0:
|
|
|
|
LEAVE()
|
|
return err;
|
|
}
|
|
|
|
static int w83792d_detach_client(struct i2c_client *client)
|
|
{
|
|
int err;
|
|
struct w83792d_data *data = client->data;
|
|
ENTER()
|
|
|
|
/* remove sysctl table (primary client only) */
|
|
if ((data))
|
|
i2c_deregister_entry(data->sysctl_id);
|
|
|
|
if ((err = i2c_detach_client(client))) {
|
|
printk(KERN_ERR "w83792d: Client deregistration failed, client not detached.\n");
|
|
LEAVE()
|
|
return err;
|
|
}
|
|
|
|
if (data) {
|
|
/* primary client */
|
|
kfree(data);
|
|
} else {
|
|
/* subclients */
|
|
kfree(client);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Called when we have found a new W83792D. */
|
|
static void w83792d_init_client(struct i2c_client *client)
|
|
{
|
|
int temp2_cfg, temp3_cfg;
|
|
u8 vid_in_b;
|
|
|
|
ENTER()
|
|
|
|
if (init) {
|
|
w83792d_write_value(client, W83792D_REG_CONFIG, 0x80);
|
|
}
|
|
/* data->vrm = 90; */ /* maybe need to be modified! */
|
|
|
|
/* Clear the bit6 of W83792D_REG_VID_IN_B(set it into 0):
|
|
W83792D_REG_VID_IN_B bit6 = 0: the high/low limit of
|
|
vin0/vin1 can be modified by user;
|
|
W83792D_REG_VID_IN_B bit6 = 1: the high/low limit of
|
|
vin0/vin1 auto-updated, can NOT be modified by user. */
|
|
vid_in_b = w83792d_read_value(client, W83792D_REG_VID_IN_B);
|
|
w83792d_write_value(client, W83792D_REG_VID_IN_B,
|
|
vid_in_b & 0xbf);
|
|
|
|
temp2_cfg = w83792d_read_value(client, W83792D_REG_TEMP_ADD[0][6]);
|
|
temp3_cfg = w83792d_read_value(client, W83792D_REG_TEMP_ADD[1][6]);
|
|
w83792d_write_value(client, W83792D_REG_TEMP_ADD[0][6],
|
|
temp2_cfg & 0xe6);
|
|
w83792d_write_value(client, W83792D_REG_TEMP_ADD[1][6],
|
|
temp3_cfg & 0xe6);
|
|
|
|
/* Start monitoring */
|
|
w83792d_write_value(client, W83792D_REG_CONFIG, (w83792d_read_value(
|
|
client, W83792D_REG_CONFIG) & 0xf7) | 0x01);
|
|
LEAVE()
|
|
}
|
|
|
|
static void w83792d_update_client(struct i2c_client *client)
|
|
{
|
|
struct w83792d_data *data = client->data;
|
|
int i, j;
|
|
u8 reg_array_tmp[4], pwm_array_tmp[7], reg_tmp;
|
|
|
|
down(&data->update_lock);
|
|
|
|
if (time_after(jiffies - data->last_updated, HZ * 3) ||
|
|
time_before(jiffies, data->last_updated) || !data->valid) {
|
|
pr_debug(KERN_DEBUG "Starting device update\n");
|
|
|
|
/* Update the voltages measured value and limits */
|
|
for (i = 0; i < 9; i++) {
|
|
data->in[i] = w83792d_read_value(client,
|
|
W83792D_REG_IN[i]);
|
|
data->in_max[i] = w83792d_read_value(client,
|
|
W83792D_REG_IN_MAX[i]);
|
|
data->in_min[i] = w83792d_read_value(client,
|
|
W83792D_REG_IN_MIN[i]);
|
|
}
|
|
data->low_bits = w83792d_read_value(client,
|
|
W83792D_REG_LOW_BITS1) +
|
|
(w83792d_read_value(client,
|
|
W83792D_REG_LOW_BITS2) << 8);
|
|
|
|
for (i = 0; i < 7; i++) {
|
|
/* Update the Fan measured value and limits */
|
|
data->fan[i] = w83792d_read_value(client,
|
|
W83792D_REG_FAN[i]);
|
|
data->fan_min[i] = w83792d_read_value(client,
|
|
W83792D_REG_FAN_MIN[i]);
|
|
/* Update the PWM/DC Value and PWM/DC flag */
|
|
pwm_array_tmp[i] = w83792d_read_value(client,
|
|
W83792D_REG_PWM[i]);
|
|
data->pwm[i] = pwm_array_tmp[i] & 0x0f;
|
|
data->pwm_flag[i] = pwm_array_tmp[i] >> 7;
|
|
}
|
|
data->fan_cfg = w83792d_read_value(client, W83792D_REG_FAN_CFG);
|
|
|
|
/* Update the Fan Divisor */
|
|
for (i = 0; i < 4; i++) {
|
|
reg_array_tmp[i] = w83792d_read_value(client, W83792D_REG_FAN_DIV[i]);
|
|
}
|
|
data->fan_div[0] = reg_array_tmp[0] & 0x07;
|
|
data->fan_div[1] = (reg_array_tmp[0] >> 4) & 0x07;
|
|
data->fan_div[2] = reg_array_tmp[1] & 0x07;
|
|
data->fan_div[3] = (reg_array_tmp[1] >> 4) & 0x07;
|
|
data->fan_div[4] = reg_array_tmp[2] & 0x07;
|
|
data->fan_div[5] = (reg_array_tmp[2] >> 4) & 0x07;
|
|
data->fan_div[6] = reg_array_tmp[3] & 0x07;
|
|
|
|
for (i = 0; i < 7; i++) {
|
|
if (!(data->has_fan & (1 << i)))
|
|
continue;
|
|
if (data->fan[i] == 0xff && data->fan_div[i] < 7)
|
|
w83792d_set_fan_div(client, i, 7);
|
|
else if (data->fan[i] < 0x70 && data->fan_div[i] > 0) {
|
|
w83792d_set_fan_div(client, i,
|
|
data->fan_div[i] - 1);
|
|
} else if (data->fan[i] > 0xf8 &&
|
|
data->fan_div[i] < 7) {
|
|
w83792d_set_fan_div(client, i,
|
|
data->fan_div[i] + 1);
|
|
}
|
|
}
|
|
|
|
/* Update the Temperature1 measured value and limits */
|
|
data->temp1[0] = w83792d_read_value(client, W83792D_REG_TEMP1);
|
|
data->temp1[1] = w83792d_read_value(client, W83792D_REG_TEMP1_OVER);
|
|
data->temp1[2] = w83792d_read_value(client, W83792D_REG_TEMP1_HYST);
|
|
|
|
/* Update the Temperature2/3 measured value and limits */
|
|
for (i = 0; i < 7; i++) {
|
|
data->temp_add[0][i] = w83792d_read_value(client,
|
|
W83792D_REG_TEMP_ADD[0][i]);
|
|
data->temp_add[1][i] = w83792d_read_value(client,
|
|
W83792D_REG_TEMP_ADD[1][i]);
|
|
}
|
|
|
|
/* Update the VID */
|
|
/* i = w83792d_read_value(client, W83792D_REG_FAN_DIV[0]);
|
|
data->vid = i & 0x0f;
|
|
data->vid |=
|
|
(w83792d_read_value(client, W83792D_REG_CHIPID) & 0x01)
|
|
<< 4; */
|
|
|
|
/* Update the realtime status */
|
|
data->alarms = w83792d_read_value(client, W83792D_REG_ALARM1) +
|
|
(w83792d_read_value(client, W83792D_REG_ALARM2) << 8) +
|
|
(w83792d_read_value(client, W83792D_REG_ALARM3) << 16);
|
|
|
|
/* Update CaseOpen status and it's CLR_CHS. */
|
|
data->chassis[0] = (w83792d_read_value(client,
|
|
W83792D_REG_CASE_OPEN)
|
|
>> 5) & 0x01;
|
|
data->chassis[1] = (w83792d_read_value(client,
|
|
W83792D_REG_CASE_OPEN_CLR)
|
|
>> 7) & 0x01;
|
|
|
|
/* Update Thermal Cruise/Smart Fan I target value */
|
|
for (i = 0; i < 3; i++) {
|
|
data->thermal_cruise[i] =
|
|
w83792d_read_value(client,
|
|
W83792D_REG_THERMAL[i]) & 0x7f;
|
|
}
|
|
|
|
/* Update Smart Fan I/II tolerance */
|
|
reg_tmp = w83792d_read_value(client, W83792D_REG_FAN_TOL[0]);
|
|
data->fan_tolerance[0] = reg_tmp & 0x0f;
|
|
data->fan_tolerance[1] = (reg_tmp >> 4) & 0x0f;
|
|
data->fan_tolerance[2] =
|
|
w83792d_read_value(client, W83792D_REG_FAN_TOL[2]) & 0x0f;
|
|
|
|
/* Update Smart Fan II temperature points */
|
|
for (i = 0; i < 3; i++) {
|
|
for (j = 0; j < 4; j++) {
|
|
data->sf2_points[i][j] = w83792d_read_value(
|
|
client,W83792D_REG_POINTS[i][j]) & 0x7f;
|
|
}
|
|
}
|
|
|
|
/* Update Smart Fan II duty cycle levels */
|
|
for (i = 0; i < 3; i++) {
|
|
reg_tmp = w83792d_read_value(client,
|
|
W83792D_REG_LEVELS[i][0]);
|
|
data->sf2_levels[i][0] = reg_tmp & 0x0f;
|
|
data->sf2_levels[i][1] = (reg_tmp >> 4) & 0x0f;
|
|
reg_tmp = w83792d_read_value(client,
|
|
W83792D_REG_LEVELS[i][2]);
|
|
data->sf2_levels[i][2] = (reg_tmp >> 4) & 0x0f;
|
|
data->sf2_levels[i][3] = reg_tmp & 0x0f;
|
|
}
|
|
data->last_updated = jiffies;
|
|
data->valid = 1;
|
|
#ifdef W83792D_DEBUG
|
|
w83792d_print_debug(data);
|
|
#endif
|
|
}
|
|
up(&data->update_lock);
|
|
}
|
|
|
|
/* This is a function used to debug the message. */
|
|
#ifdef W83792D_DEBUG
|
|
static void w83792d_print_debug(struct w83792d_data *data)
|
|
{
|
|
int i=0, j=0;
|
|
printk(KERN_DEBUG "==========The following is the debug message...========\n");
|
|
printk(KERN_DEBUG "9 set of Voltages: =====>\n");
|
|
for (i=0; i<=8; i++) {
|
|
printk(KERN_DEBUG "vin[%d] is: 0x%x\n", i, data->in[i]);
|
|
printk(KERN_DEBUG "vin[%d] max is: 0x%x\n", i, data->in_max[i]);
|
|
printk(KERN_DEBUG "vin[%d] min is: 0x%x\n", i, data->in_min[i]);
|
|
}
|
|
printk(KERN_DEBUG "Low Bit1 is: 0x%x\n", data->low_bits & 0xff);
|
|
printk(KERN_DEBUG "Low Bit2 is: 0x%x\n", data->low_bits >> 8);
|
|
printk(KERN_DEBUG "7 set of Fan Counts and 3 set of Duty Cycles: =====>\n");
|
|
printk(KERN_DEBUG "fan_cfg is: 0x%x\n", data->fan_cfg);
|
|
for (i=0; i<=6; i++) {
|
|
printk(KERN_DEBUG "fan[%d] is: 0x%x\n", i, data->fan[i]);
|
|
printk(KERN_DEBUG "fan[%d] min is: 0x%x\n", i, data->fan_min[i]);
|
|
if (i<3) {
|
|
printk(KERN_DEBUG "pwm[%d] is: 0x%x\n", i, data->pwm[i]);
|
|
printk(KERN_DEBUG "pwm_flag[%d] is: 0x%x\n", i, data->pwm_flag[i]);
|
|
}
|
|
}
|
|
printk(KERN_DEBUG "3 set of Temperatures: =====>\n");
|
|
printk(KERN_DEBUG "temp1 is: 0x%x\n", data->temp1[0]);
|
|
printk(KERN_DEBUG "temp1 high limit is: 0x%x\n", data->temp1[1]);
|
|
printk(KERN_DEBUG "temp1 low limit is: 0x%x\n", data->temp1[2]);
|
|
for (i=0; i<2; i++) {
|
|
for (j=0; j<7; j++) {
|
|
printk(KERN_DEBUG "temp_add[%d][%d] is: 0x%x\n", i, j,
|
|
data->temp_add[i][j]);
|
|
}
|
|
}
|
|
for (i=0; i<=6; i++) {
|
|
printk(KERN_DEBUG "fan_div[%d] is: 0x%x\n", i, data->fan_div[i]);
|
|
}
|
|
printk(KERN_DEBUG "==========End of the debug message...==================\n\n");
|
|
}
|
|
#endif
|
|
|
|
/* 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. */
|
|
|
|
/* read/write voltage meaured value and limits */
|
|
static void w83792d_in(struct i2c_client *client, int operation, int ctl_name,
|
|
int *nrels_mag, long *results)
|
|
{
|
|
struct w83792d_data *data = client->data;
|
|
int nr = ctl_name - W83792D_SYSCTL_IN0;
|
|
|
|
/* result[0]: low limit, result[1]: high limit,
|
|
result[2]: measured value */
|
|
if (operation == SENSORS_PROC_REAL_INFO)
|
|
*nrels_mag = 3;
|
|
else if (operation == SENSORS_PROC_REAL_READ) {
|
|
w83792d_update_client(client);
|
|
results[0] = IN_FROM_REG(nr, data->in_min[nr]*4);
|
|
results[1] = IN_FROM_REG(nr, data->in_max[nr]*4);
|
|
/* in7 and in8 do not have low bits, but the formula still
|
|
works */
|
|
results[2] = IN_FROM_REG(nr, ((data->in[nr] << 2) |
|
|
((data->low_bits >> (2 * nr))
|
|
& 0x03)));
|
|
*nrels_mag = 3;
|
|
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
|
if (*nrels_mag >= 1) {
|
|
/* Write Low limit into register. */
|
|
data->in_min[nr] = SENSORS_LIMIT(IN_TO_REG(nr,results[0])/4,
|
|
0, 255);
|
|
w83792d_write_value(client, W83792D_REG_IN_MIN[nr],
|
|
data->in_min[nr]);
|
|
}
|
|
if (*nrels_mag >= 2) {
|
|
/* Write High limit into register. */
|
|
data->in_max[nr] = SENSORS_LIMIT(IN_TO_REG(nr,results[1])/4,
|
|
0, 255);
|
|
w83792d_write_value(client, W83792D_REG_IN_MAX[nr],
|
|
data->in_max[nr]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void w83792d_set_fan_div(struct i2c_client *client, int nr, u8 newdiv)
|
|
{
|
|
struct w83792d_data *data = client->data;
|
|
int min, old;
|
|
u8 tmp;
|
|
|
|
min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]));
|
|
old = FAN_FROM_REG(data->fan[nr], DIV_FROM_REG(data->fan_div[nr]));
|
|
data->fan_div[nr] = newdiv;
|
|
tmp = w83792d_read_value(client, W83792D_REG_FAN_DIV[nr >> 1]);
|
|
tmp &= (nr & 1) ? 0x8f : 0xf8;
|
|
tmp |= (nr & 1) ? ((newdiv << 4) & 0x70) : (newdiv & 0x07);
|
|
w83792d_write_value(client, W83792D_REG_FAN_DIV[nr >> 1], tmp);
|
|
data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
|
|
data->fan[nr] = FAN_TO_REG(old, DIV_FROM_REG(data->fan_div[nr]));
|
|
w83792d_write_value(client, W83792D_REG_FAN_MIN[nr],
|
|
data->fan_min[nr]);
|
|
}
|
|
|
|
/* read/write fan meaured value and limits */
|
|
static void w83792d_fan(struct i2c_client *client, int operation, int ctl_name,
|
|
int *nrels_mag, long *results)
|
|
{
|
|
struct w83792d_data *data = client->data;
|
|
int nr = ctl_name - W83792D_SYSCTL_FAN1;
|
|
|
|
/* result[0]: low limit, result[1]: measured value */
|
|
if (operation == SENSORS_PROC_REAL_INFO)
|
|
*nrels_mag = 0;
|
|
else if (operation == SENSORS_PROC_REAL_READ) {
|
|
w83792d_update_client(client);
|
|
if (data->has_fan & (1 << nr)) {
|
|
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]));
|
|
} else {
|
|
results[0] = 0;
|
|
results[1] = 0;
|
|
}
|
|
*nrels_mag = 2;
|
|
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
|
if (*nrels_mag >= 1 && (data->has_fan & (1 << nr))) {
|
|
data->fan_min[nr] = FAN_TO_REG(results[0],
|
|
DIV_FROM_REG(data->fan_div[nr]));
|
|
w83792d_write_value(client,
|
|
W83792D_REG_FAN_MIN[nr],
|
|
data->fan_min[nr]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* read/write temperature1 meaured value and limits */
|
|
static void w83792d_temp(struct i2c_client *client, int operation, int ctl_name,
|
|
int *nrels_mag, long *results)
|
|
{
|
|
struct w83792d_data *data = client->data;
|
|
|
|
/* result[0]: high limit, result[1]: low limit
|
|
result[2]: measured value, the order is different with voltage(in) */
|
|
if (operation == SENSORS_PROC_REAL_INFO) {
|
|
*nrels_mag = 1;
|
|
} else if (operation == SENSORS_PROC_REAL_READ) {
|
|
w83792d_update_client(client);
|
|
results[0] = TEMP_FROM_REG(data->temp1[1]);
|
|
results[1] = TEMP_FROM_REG(data->temp1[2]);
|
|
results[2] = TEMP_FROM_REG(data->temp1[0]);
|
|
*nrels_mag = 3;
|
|
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
|
if (*nrels_mag >= 1) {
|
|
data->temp1[1] = TEMP_TO_REG(results[0]);
|
|
w83792d_write_value(client, W83792D_REG_TEMP1_OVER,
|
|
data->temp1[1]);
|
|
}
|
|
if (*nrels_mag >= 2) {
|
|
data->temp1[2] = TEMP_TO_REG(results[1]);
|
|
w83792d_write_value(client, W83792D_REG_TEMP1_HYST,
|
|
data->temp1[2]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* read/write temperature2,3 meaured value and limits */
|
|
static void w83792d_temp_add(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results)
|
|
{
|
|
struct w83792d_data *data = client->data;
|
|
int nr = ctl_name - W83792D_SYSCTL_TEMP2;
|
|
int i=0, j=0;
|
|
|
|
/* result[0]: high limit, result[1]: low limit
|
|
result[2]: measured value, the order is different with voltage(in) */
|
|
if (operation == SENSORS_PROC_REAL_INFO)
|
|
*nrels_mag = 1;
|
|
else if (operation == SENSORS_PROC_REAL_READ) {
|
|
w83792d_update_client(client);
|
|
for (i=0; i<3; i++) {
|
|
j = (i==0) ? 2 : ((i==1)?0:1);
|
|
if ((data->temp_add[nr][i*2+1]) & 0x80) {
|
|
results[j] = TEMP_FROM_REG(data->temp_add[nr][i*2]) + 5;
|
|
} else {
|
|
results[j] = TEMP_FROM_REG(data->temp_add[nr][i*2]);
|
|
}
|
|
}
|
|
*nrels_mag = 3;
|
|
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
|
if (*nrels_mag >= 1) {
|
|
data->temp_add[nr][2] = TEMP_TO_REG(results[0]);
|
|
w83792d_write_value(client,
|
|
W83792D_REG_TEMP_ADD[nr][2],
|
|
data->temp_add[nr][2]);
|
|
if ((results[0]%10) == 0) {
|
|
w83792d_write_value(client,
|
|
W83792D_REG_TEMP_ADD[nr][3], 0x00);
|
|
} else { /* consider the 0.5 degree */
|
|
w83792d_write_value(client,
|
|
W83792D_REG_TEMP_ADD[nr][3], 0x80);
|
|
}
|
|
}
|
|
if (*nrels_mag >= 2) {
|
|
data->temp_add[nr][4] = TEMP_TO_REG(results[1]);
|
|
w83792d_write_value(client,
|
|
W83792D_REG_TEMP_ADD[nr][4],
|
|
data->temp_add[nr][4]);
|
|
if ((results[1]%10) == 0) {
|
|
w83792d_write_value(client,
|
|
W83792D_REG_TEMP_ADD[nr][5], 0x00);
|
|
} else { /* consider the 0.5 degree */
|
|
w83792d_write_value(client,
|
|
W83792D_REG_TEMP_ADD[nr][5], 0x80);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
void w83792d_vid(struct i2c_client *client, int operation, int ctl_name,
|
|
int *nrels_mag, long *results)
|
|
{
|
|
struct w83792d_data *data = client->data;
|
|
if (operation == SENSORS_PROC_REAL_INFO)
|
|
*nrels_mag = 3;
|
|
else if (operation == SENSORS_PROC_REAL_READ) {
|
|
w83792d_update_client(client);
|
|
results[0] = vid_from_reg(data->vid, data->vrm);
|
|
*nrels_mag = 1;
|
|
}
|
|
}
|
|
|
|
void w83792d_vrm(struct i2c_client *client, int operation, int ctl_name,
|
|
int *nrels_mag, long *results)
|
|
{
|
|
struct w83792d_data *data = client->data;
|
|
if (operation == SENSORS_PROC_REAL_INFO)
|
|
*nrels_mag = 1;
|
|
else if (operation == SENSORS_PROC_REAL_READ) {
|
|
results[0] = data->vrm;
|
|
*nrels_mag = 1;
|
|
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
|
if (*nrels_mag >= 1) {
|
|
data->vrm = results[0];
|
|
}
|
|
}
|
|
} */
|
|
|
|
/* Read/Write Fan Divisor */
|
|
static void w83792d_fan_div(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results)
|
|
{
|
|
struct w83792d_data *data = client->data;
|
|
int i = 0;
|
|
|
|
if (operation == SENSORS_PROC_REAL_INFO)
|
|
*nrels_mag = 0;
|
|
else if (operation == SENSORS_PROC_REAL_READ) {
|
|
w83792d_update_client(client);
|
|
for (i=0; i<7; i++) {
|
|
results[i] = DIV_FROM_REG(data->fan_div[i]);
|
|
}
|
|
*nrels_mag = 7;
|
|
}
|
|
}
|
|
|
|
|
|
/* Under Smart Fan I mode: read/write the Fan1/2/3 target temperature */
|
|
static void w83792d_thermal_cruise(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results)
|
|
{
|
|
struct w83792d_data *data = client->data;
|
|
int i=0;
|
|
u8 target_tmp=0, target_mask=0;
|
|
|
|
if (operation == SENSORS_PROC_REAL_INFO)
|
|
*nrels_mag = 0;
|
|
else if (operation == SENSORS_PROC_REAL_READ) {
|
|
w83792d_update_client(client);
|
|
for (i=0; i<3; i++) {
|
|
results[i] = data->thermal_cruise[i];
|
|
}
|
|
*nrels_mag = 3;
|
|
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
|
for (i=0; i<3; i++) {
|
|
if (*nrels_mag < (i+1)) {
|
|
return;
|
|
}
|
|
target_tmp = results[i];
|
|
target_tmp = target_tmp & 0x7f;
|
|
target_mask = w83792d_read_value(client,
|
|
W83792D_REG_THERMAL[i]) & 0x80;
|
|
data->thermal_cruise[i] = SENSORS_LIMIT(target_tmp, 0, 255);
|
|
w83792d_write_value(client, W83792D_REG_THERMAL[i],
|
|
(data->thermal_cruise[i])|target_mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* The tolerance of fan1/fan2/fan3, when using Thermal Cruise(Smart Fan I)
|
|
or Smart Fan II mode. */
|
|
static void w83792d_fan_tolerance(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results)
|
|
{
|
|
struct w83792d_data *data = client->data;
|
|
int i=0;
|
|
u8 tol_tmp, tol_mask;
|
|
|
|
if (operation == SENSORS_PROC_REAL_INFO)
|
|
*nrels_mag = 0;
|
|
else if (operation == SENSORS_PROC_REAL_READ) {
|
|
w83792d_update_client(client);
|
|
for (i=0; i<3; i++) {
|
|
results[i] = data->fan_tolerance[i];
|
|
}
|
|
*nrels_mag = 3;
|
|
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
|
for (i=0; i<3; i++) {
|
|
if (*nrels_mag < (i+1)) {
|
|
return;
|
|
}
|
|
tol_mask = w83792d_read_value(client,
|
|
W83792D_REG_FAN_TOL[i]) & ((i==1)?0x0f:0xf0);
|
|
tol_tmp = SENSORS_LIMIT(results[i], 0, 15);
|
|
tol_tmp &= 0x0f;
|
|
data->fan_tolerance[i] = tol_tmp;
|
|
if (i==1) {
|
|
tol_tmp <<= 4;
|
|
}
|
|
w83792d_write_value(client, W83792D_REG_FAN_TOL[i],
|
|
tol_mask|tol_tmp);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Under Smart Fan II mode: read/write the Fan1/2/3 temperature points */
|
|
static void w83792d_sf2_points(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results)
|
|
{
|
|
struct w83792d_data *data = client->data;
|
|
int nr = ctl_name - W83792D_SYSCTL_SF2_POINTS_FAN1;
|
|
int j=0;
|
|
u8 mask_tmp = 0;
|
|
|
|
if (operation == SENSORS_PROC_REAL_INFO)
|
|
*nrels_mag = 0;
|
|
else if (operation == SENSORS_PROC_REAL_READ) {
|
|
w83792d_update_client(client);
|
|
for (j=0; j<4; j++) {
|
|
results[j] = data->sf2_points[nr][j];
|
|
}
|
|
*nrels_mag = 4;
|
|
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
|
for (j=0; j<4; j++) {
|
|
if (*nrels_mag < (j+1)) {
|
|
return;
|
|
}
|
|
data->sf2_points[nr][j] = SENSORS_LIMIT(results[j],
|
|
0, 127);
|
|
mask_tmp = w83792d_read_value(client,
|
|
W83792D_REG_POINTS[nr][j]) & 0x80;
|
|
w83792d_write_value(client, W83792D_REG_POINTS[nr][j],
|
|
mask_tmp|data->sf2_points[nr][j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Smart Fan II Duty Cycle1/2/3 of Fan1/2/3.
|
|
Notice that: The Non-Stop can NOT be modified by user,
|
|
because it is related with some physical characters,
|
|
usually set by BIOS. User's modification to it may lead to
|
|
Fan's stop, then bring danger. */
|
|
static void w83792d_sf2_levels(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results)
|
|
{
|
|
struct w83792d_data *data = client->data;
|
|
int nr = ctl_name - W83792D_SYSCTL_SF2_LEVELS_FAN1;
|
|
int j = 0;
|
|
u8 mask_tmp = 0, level_tmp = 0;
|
|
|
|
if (operation == SENSORS_PROC_REAL_INFO)
|
|
*nrels_mag = 0;
|
|
else if (operation == SENSORS_PROC_REAL_READ) {
|
|
w83792d_update_client(client);
|
|
for (j=0; j<4; j++) {
|
|
results[j] = (data->sf2_levels[nr][j] * 100) / 15;
|
|
}
|
|
*nrels_mag = 4;
|
|
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
|
for (j=1; j<4; j++) { /* start with 1: need ignore Non-Stop */
|
|
if (*nrels_mag < j) {
|
|
return;
|
|
}
|
|
data->sf2_levels[nr][j] =
|
|
SENSORS_LIMIT((results[j]*15)/100, 0, 15);
|
|
mask_tmp = w83792d_read_value(client, W83792D_REG_LEVELS[nr][j])
|
|
& ((j==3) ? 0xf0 : 0x0f);
|
|
if (j==3) {
|
|
level_tmp = data->sf2_levels[nr][j];
|
|
} else {
|
|
level_tmp = data->sf2_levels[nr][j] << 4;
|
|
}
|
|
w83792d_write_value(client, W83792D_REG_LEVELS[nr][j],
|
|
level_tmp | mask_tmp);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* get reatime status of all sensors items: voltage, temp, fan */
|
|
static void w83792d_alarms(struct i2c_client *client, int operation, int ctl_name,
|
|
int *nrels_mag, long *results)
|
|
{
|
|
struct w83792d_data *data = client->data;
|
|
if (operation == SENSORS_PROC_REAL_INFO)
|
|
*nrels_mag = 0;
|
|
else if (operation == SENSORS_PROC_REAL_READ) {
|
|
w83792d_update_client(client);
|
|
results[0] = data->alarms;
|
|
*nrels_mag = 1;
|
|
}
|
|
}
|
|
|
|
/* Read/Write Chassis status and Reset Chassis. */
|
|
static void w83792d_chassis(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results)
|
|
{
|
|
struct w83792d_data *data = client->data;
|
|
u8 temp1 = 0, temp2 = 0;
|
|
|
|
if (operation == SENSORS_PROC_REAL_INFO)
|
|
*nrels_mag = 0;
|
|
else if (operation == SENSORS_PROC_REAL_READ) {
|
|
w83792d_update_client(client);
|
|
results[0] = data->chassis[0];
|
|
results[1] = data->chassis[1];
|
|
*nrels_mag = 2;
|
|
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
|
data->chassis[1] = SENSORS_LIMIT(results[1], 0 ,1);
|
|
temp1 = ((data->chassis[1]) << 7) & 0x80;
|
|
temp2 = w83792d_read_value(client,
|
|
W83792D_REG_CASE_OPEN_CLR) & 0x7f;
|
|
w83792d_write_value(client,
|
|
W83792D_REG_CASE_OPEN_CLR,
|
|
temp1|temp2);
|
|
}
|
|
}
|
|
|
|
/* Read/Write PWM/DC value of Fan1,Fan2,Fan3, which controls the
|
|
Fan Duty Cycle */
|
|
static void w83792d_pwm(struct i2c_client *client, int operation, int ctl_name,
|
|
int *nrels_mag, long *results)
|
|
{
|
|
struct w83792d_data *data = client->data;
|
|
int nr = ctl_name - W83792D_SYSCTL_PWM1;
|
|
u8 pwm_mask;
|
|
|
|
if (operation == SENSORS_PROC_REAL_INFO)
|
|
*nrels_mag = 0;
|
|
else if (operation == SENSORS_PROC_REAL_READ) {
|
|
w83792d_update_client(client);
|
|
results[0] = data->pwm[nr] << 4;
|
|
*nrels_mag = 1;
|
|
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
|
data->pwm[nr] = SENSORS_LIMIT(results[0], 0, 255) >> 4;
|
|
pwm_mask = w83792d_read_value(client,W83792D_REG_PWM[nr]) & 0xf0;
|
|
w83792d_write_value(client,W83792D_REG_PWM[nr],pwm_mask|data->pwm[nr]);
|
|
}
|
|
}
|
|
|
|
/* Read/Write PWM/DC mode for Fan1,Fan2,Fan3:
|
|
1->PWM mode, 0->DC mode */
|
|
static void w83792d_pwm_flag(struct i2c_client *client, int operation, int ctl_name,
|
|
int *nrels_mag, long *results)
|
|
{
|
|
struct w83792d_data *data = client->data;
|
|
int i = 0;
|
|
u8 pwm_flag_mask;
|
|
|
|
if (operation == SENSORS_PROC_REAL_INFO)
|
|
*nrels_mag = 0;
|
|
else if (operation == SENSORS_PROC_REAL_READ) {
|
|
w83792d_update_client(client);
|
|
for (i=0; i<3; i++) {
|
|
results[i] = data->pwm_flag[i];
|
|
}
|
|
*nrels_mag = 3;
|
|
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
|
for (i=0; i<3; i++) {
|
|
if (*nrels_mag < (i+1)) {
|
|
return;
|
|
}
|
|
data->pwm_flag[i] = SENSORS_LIMIT(results[i], 0, 1);
|
|
pwm_flag_mask = w83792d_read_value(client,
|
|
W83792D_REG_PWM[i]) & 0x7f;
|
|
w83792d_write_value(client, W83792D_REG_PWM[i],
|
|
((data->pwm_flag[i])<<7)|pwm_flag_mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Read/Write Fan mode into:PWM/DC, Thermal Cruise(SmartFanI), SmartFanII
|
|
0->PWM/DC mode, 1->Thermal Cruise mode, 2/3->SmartFanII mode */
|
|
static void w83792d_fan_cfg(struct i2c_client *client, int operation,
|
|
int ctl_name, int *nrels_mag, long *results)
|
|
{
|
|
struct w83792d_data *data = client->data;
|
|
u8 temp_cfg1, temp_cfg2, temp_cfg3, temp_cfg4;
|
|
|
|
if (operation == SENSORS_PROC_REAL_INFO)
|
|
*nrels_mag = 0;
|
|
else if (operation == SENSORS_PROC_REAL_READ) {
|
|
w83792d_update_client(client);
|
|
results[0] = (data->fan_cfg) & 0x03; /* Fan1's Mode */
|
|
results[1] = ((data->fan_cfg)>>2) & 0x03; /* Fan2's Mode */
|
|
results[2] = ((data->fan_cfg)>>4) & 0x03; /* Fan3's Mode */
|
|
*nrels_mag = 3;
|
|
} else if (operation == SENSORS_PROC_REAL_WRITE) {
|
|
if (*nrels_mag < 3) {
|
|
return;
|
|
}
|
|
temp_cfg1 = SENSORS_LIMIT(results[0], 0, 3);
|
|
temp_cfg2 = SENSORS_LIMIT(results[1], 0, 3) << 2;
|
|
temp_cfg3 = SENSORS_LIMIT(results[2], 0, 3) << 4;
|
|
temp_cfg4 = w83792d_read_value(client,W83792D_REG_FAN_CFG) & 0xc0;
|
|
data->fan_cfg = ((temp_cfg4|temp_cfg3)|temp_cfg2)|temp_cfg1;
|
|
w83792d_write_value(client,W83792D_REG_FAN_CFG,data->fan_cfg);
|
|
}
|
|
}
|
|
|
|
static int __init sm_w83792d_init(void)
|
|
{
|
|
ENTER()
|
|
|
|
printk(KERN_INFO "w83792d version %s (%s)\n", LM_VERSION, LM_DATE);
|
|
|
|
LEAVE()
|
|
return i2c_add_driver(&w83792d_driver);
|
|
}
|
|
|
|
static void __exit sm_w83792d_exit(void)
|
|
{
|
|
ENTER()
|
|
|
|
i2c_del_driver(&w83792d_driver);
|
|
|
|
LEAVE()
|
|
}
|
|
|
|
|
|
MODULE_AUTHOR("Chunhao Huang @ Winbond");
|
|
MODULE_DESCRIPTION("W83792AD/D driver for linux-2.4");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
module_init(sm_w83792d_init);
|
|
module_exit(sm_w83792d_exit);
|
|
|