| 1 | /* |
|---|
| 2 | w83792d.c - Part of lm_sensors, Linux kernel modules for hardware |
|---|
| 3 | monitoring |
|---|
| 4 | Copyright (c) 2004, 2005 Winbond Electronics Corp. |
|---|
| 5 | Chunhao Huang |
|---|
| 6 | |
|---|
| 7 | This program is free software; you can redistribute it and/or modify |
|---|
| 8 | it under the terms of the GNU General Public License as published by |
|---|
| 9 | the Free Software Foundation; either version 2 of the License, or |
|---|
| 10 | (at your option) any later version. |
|---|
| 11 | |
|---|
| 12 | This program is distributed in the hope that it will be useful, |
|---|
| 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 15 | GNU General Public License for more details. |
|---|
| 16 | |
|---|
| 17 | You should have received a copy of the GNU General Public License |
|---|
| 18 | along with this program; if not, write to the Free Software |
|---|
| 19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
|---|
| 20 | |
|---|
| 21 | Note: |
|---|
| 22 | 1. This driver is only for 2.4 kernel(2.4.10 or later), 2.6 kernel |
|---|
| 23 | need a different driver. |
|---|
| 24 | 2. This driver is only for Winbond W83792D C version device, there |
|---|
| 25 | are also some motherboards with B version W83792D device. The |
|---|
| 26 | calculation method to in6-in7(measured value, limits) is a little |
|---|
| 27 | different between C and B version. C or B version can be identified |
|---|
| 28 | by CR[0x49h]. |
|---|
| 29 | 3. The function of chassis open detection need further test. |
|---|
| 30 | 4. The function of vid and vrm has not been finished, because I'm NOT |
|---|
| 31 | very familiar with them. If someone can finish it, that's good, |
|---|
| 32 | then please delete this note 4. |
|---|
| 33 | */ |
|---|
| 34 | |
|---|
| 35 | /* |
|---|
| 36 | Supports following chips: |
|---|
| 37 | |
|---|
| 38 | Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA |
|---|
| 39 | w83792d 9 7 3 3 0x7a 0x5ca3 yes no |
|---|
| 40 | */ |
|---|
| 41 | |
|---|
| 42 | #include <linux/module.h> |
|---|
| 43 | #include <linux/slab.h> |
|---|
| 44 | #include <linux/i2c.h> |
|---|
| 45 | #include <linux/i2c-proc.h> |
|---|
| 46 | #include <linux/init.h> |
|---|
| 47 | #include "version.h" |
|---|
| 48 | #include "sensors_vid.h" |
|---|
| 49 | |
|---|
| 50 | /* Addresses to scan */ |
|---|
| 51 | static unsigned short normal_i2c[] = { SENSORS_I2C_END }; |
|---|
| 52 | static unsigned short normal_i2c_range[] = { 0x2c, 0x2f, SENSORS_I2C_END }; |
|---|
| 53 | static unsigned int normal_isa[] = { SENSORS_ISA_END }; |
|---|
| 54 | static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; |
|---|
| 55 | |
|---|
| 56 | /* Insmod parameters */ |
|---|
| 57 | SENSORS_INSMOD_1(w83792d); |
|---|
| 58 | SENSORS_MODULE_PARM(force_subclients, "List of subclient addresses: " \ |
|---|
| 59 | "{bus, clientaddr, subclientaddr1, subclientaddr2}"); |
|---|
| 60 | |
|---|
| 61 | static int init; |
|---|
| 62 | MODULE_PARM(init, "i"); |
|---|
| 63 | MODULE_PARM_DESC(init, "Set to one for chip initialization"); |
|---|
| 64 | |
|---|
| 65 | /* Enable/Disable w83792d debugging output */ |
|---|
| 66 | /* #define W83792D_DEBUG 1 */ |
|---|
| 67 | |
|---|
| 68 | /* Constants specified below */ |
|---|
| 69 | #define W83792D_REG_GPIO_EN 0x1A |
|---|
| 70 | #define W83792D_REG_CONFIG 0x40 |
|---|
| 71 | #define W83792D_REG_I2C_ADDR 0x48 |
|---|
| 72 | #define W83792D_REG_CHIPID 0x49 /* contains version ID: A/B/C */ |
|---|
| 73 | #define W83792D_REG_I2C_SUBADDR 0x4A |
|---|
| 74 | #define W83792D_REG_PIN 0x4B |
|---|
| 75 | #define W83792D_REG_IRQ 0x4C |
|---|
| 76 | #define W83792D_REG_BANK 0x4E |
|---|
| 77 | #define W83792D_REG_CHIPMAN 0x4F /* contains the vendor ID */ |
|---|
| 78 | #define W83792D_REG_WCHIPID 0x58 /* contains the chip ID */ |
|---|
| 79 | #define W83792D_REG_VID_IN_B 0x17 /* ctroll in0/in1 's limit modifiability */ |
|---|
| 80 | |
|---|
| 81 | static const u8 W83792D_REG_IN[9] = { |
|---|
| 82 | 0x20, /* Vcore A in DataSheet */ |
|---|
| 83 | 0x21, /* Vcore B in DataSheet */ |
|---|
| 84 | 0x22, /* VIN0 in DataSheet */ |
|---|
| 85 | 0x23, /* VIN1 in DataSheet */ |
|---|
| 86 | 0x24, /* VIN2 in DataSheet */ |
|---|
| 87 | 0x25, /* VIN3 in DataSheet */ |
|---|
| 88 | 0x26, /* 5VCC in DataSheet */ |
|---|
| 89 | 0xB0, /* 5VSB in DataSheet */ |
|---|
| 90 | 0xB1 /* VBAT in DataSheet */ |
|---|
| 91 | }; |
|---|
| 92 | #define W83792D_REG_LOW_BITS1 0x3E /* Low Bits I in DataSheet */ |
|---|
| 93 | #define W83792D_REG_LOW_BITS2 0x3F /* Low Bits II in DataSheet */ |
|---|
| 94 | static const u8 W83792D_REG_IN_MAX[9] = { |
|---|
| 95 | 0x2B, /* Vcore A High Limit in DataSheet */ |
|---|
| 96 | 0x2D, /* Vcore B High Limit in DataSheet */ |
|---|
| 97 | 0x2F, /* VIN0 High Limit in DataSheet */ |
|---|
| 98 | 0x31, /* VIN1 High Limit in DataSheet */ |
|---|
| 99 | 0x33, /* VIN2 High Limit in DataSheet */ |
|---|
| 100 | 0x35, /* VIN3 High Limit in DataSheet */ |
|---|
| 101 | 0x37, /* 5VCC High Limit in DataSheet */ |
|---|
| 102 | 0xB4, /* 5VSB High Limit in DataSheet */ |
|---|
| 103 | 0xB6 /* VBAT High Limit in DataSheet */ |
|---|
| 104 | }; |
|---|
| 105 | static const u8 W83792D_REG_IN_MIN[9] = { |
|---|
| 106 | 0x2C, /* Vcore A Low Limit in DataSheet */ |
|---|
| 107 | 0x2E, /* Vcore B Low Limit in DataSheet */ |
|---|
| 108 | 0x30, /* VIN0 Low Limit in DataSheet */ |
|---|
| 109 | 0x32, /* VIN1 Low Limit in DataSheet */ |
|---|
| 110 | 0x34, /* VIN2 Low Limit in DataSheet */ |
|---|
| 111 | 0x36, /* VIN3 Low Limit in DataSheet */ |
|---|
| 112 | 0x38, /* 5VCC Low Limit in DataSheet */ |
|---|
| 113 | 0xB5, /* 5VSB Low Limit in DataSheet */ |
|---|
| 114 | 0xB7 /* VBAT Low Limit in DataSheet */ |
|---|
| 115 | }; |
|---|
| 116 | |
|---|
| 117 | static const u8 W83792D_REG_FAN[7] = { |
|---|
| 118 | 0x28, /* FAN 1 Count in DataSheet */ |
|---|
| 119 | 0x29, /* FAN 2 Count in DataSheet */ |
|---|
| 120 | 0x2A, /* FAN 3 Count in DataSheet */ |
|---|
| 121 | 0xB8, /* FAN 4 Count in DataSheet */ |
|---|
| 122 | 0xB9, /* FAN 5 Count in DataSheet */ |
|---|
| 123 | 0xBA, /* FAN 6 Count in DataSheet */ |
|---|
| 124 | 0xBE /* FAN 7 Count in DataSheet */ |
|---|
| 125 | }; |
|---|
| 126 | static const u8 W83792D_REG_FAN_MIN[7] = { |
|---|
| 127 | 0x3B, /* FAN 1 Count Low Limit in DataSheet */ |
|---|
| 128 | 0x3C, /* FAN 2 Count Low Limit in DataSheet */ |
|---|
| 129 | 0x3D, /* FAN 3 Count Low Limit in DataSheet */ |
|---|
| 130 | 0xBB, /* FAN 4 Count Low Limit in DataSheet */ |
|---|
| 131 | 0xBC, /* FAN 5 Count Low Limit in DataSheet */ |
|---|
| 132 | 0xBD, /* FAN 6 Count Low Limit in DataSheet */ |
|---|
| 133 | 0xBF /* FAN 7 Count Low Limit in DataSheet */ |
|---|
| 134 | }; |
|---|
| 135 | #define W83792D_REG_FAN_CFG 0x84 /* FAN Configuration in DataSheet */ |
|---|
| 136 | static const u8 W83792D_REG_PWM[7] = { |
|---|
| 137 | 0x81, /* FAN 1 Duty Cycle, be used to control */ |
|---|
| 138 | 0x83, /* FAN 2 Duty Cycle, be used to control */ |
|---|
| 139 | 0x94, /* FAN 3 Duty Cycle, be used to control */ |
|---|
| 140 | 0xA3, /* FAN 4 Duty Cycle, be used to control */ |
|---|
| 141 | 0xA4, /* FAN 5 Duty Cycle, be used to control */ |
|---|
| 142 | 0xA5, /* FAN 6 Duty Cycle, be used to control */ |
|---|
| 143 | 0xA6 /* FAN 7 Duty Cycle, be used to control */ |
|---|
| 144 | }; |
|---|
| 145 | |
|---|
| 146 | #define W83792D_REG_TEMP1 0x27 /* TEMP 1 in DataSheet */ |
|---|
| 147 | #define W83792D_REG_TEMP1_OVER 0x39 /* TEMP 1 High Limit in DataSheet */ |
|---|
| 148 | #define W83792D_REG_TEMP1_HYST 0x3A /* TEMP 1 Low Limit in DataSheet */ |
|---|
| 149 | static const u8 W83792D_REG_TEMP_ADD[2][7] = { |
|---|
| 150 | { 0xC0, /* TEMP 2 in DataSheet */ |
|---|
| 151 | 0xC1, /* TEMP 2(0.5 deg) in DataSheet */ |
|---|
| 152 | 0xC5, /* TEMP 2 Over High part in DataSheet */ |
|---|
| 153 | 0xC6, /* TEMP 2 Over Low part in DataSheet */ |
|---|
| 154 | 0xC3, /* TEMP 2 Thyst High part in DataSheet */ |
|---|
| 155 | 0xC4, /* TEMP 2 Thyst Low part in DataSheet */ |
|---|
| 156 | 0xC2 }, /* TEMP 2 Config in DataSheet */ |
|---|
| 157 | { 0xC8, /* TEMP 3 in DataSheet */ |
|---|
| 158 | 0xC9, /* TEMP 3(0.5 deg) in DataSheet */ |
|---|
| 159 | 0xCD, /* TEMP 3 Over High part in DataSheet */ |
|---|
| 160 | 0xCE, /* TEMP 3 Over Low part in DataSheet */ |
|---|
| 161 | 0xCB, /* TEMP 3 Thyst High part in DataSheet */ |
|---|
| 162 | 0xCC, /* TEMP 3 Thyst Low part in DataSheet */ |
|---|
| 163 | 0xCA } /* TEMP 3 Config in DataSheet */ |
|---|
| 164 | }; |
|---|
| 165 | |
|---|
| 166 | static const u8 W83792D_REG_FAN_DIV[4] = { |
|---|
| 167 | 0x47, /* contains FAN2 and FAN1 Divisor */ |
|---|
| 168 | 0x5B, /* contains FAN4 and FAN3 Divisor */ |
|---|
| 169 | 0x5C, /* contains FAN6 and FAN5 Divisor */ |
|---|
| 170 | 0x9E /* contains FAN7 Divisor. */ |
|---|
| 171 | }; |
|---|
| 172 | |
|---|
| 173 | #define W83792D_REG_ALARM1 0xA9 /* realtime status register1 */ |
|---|
| 174 | #define W83792D_REG_ALARM2 0xAA /* realtime status register2 */ |
|---|
| 175 | #define W83792D_REG_ALARM3 0xAB /* realtime status register3 */ |
|---|
| 176 | #define W83792D_REG_CASE_OPEN 0x42 /* Bit 5: Case Open status bit */ |
|---|
| 177 | #define W83792D_REG_CASE_OPEN_CLR 0x44 /* Bit 7: Case Open CLR_CHS/Reset bit */ |
|---|
| 178 | |
|---|
| 179 | static const u8 W83792D_REG_THERMAL[3] = { |
|---|
| 180 | 0x85, /* SmartFanI: Fan1 target value */ |
|---|
| 181 | 0x86, /* SmartFanI: Fan2 target value */ |
|---|
| 182 | 0x96 /* SmartFanI: Fan3 target value */ |
|---|
| 183 | }; |
|---|
| 184 | |
|---|
| 185 | static const u8 W83792D_REG_FAN_TOL[3] = { |
|---|
| 186 | 0x87, /* (bit3-0)SmartFan Fan1 tolerance */ |
|---|
| 187 | 0x87, /* (bit7-4)SmartFan Fan2 tolerance */ |
|---|
| 188 | 0x97 /* (bit3-0)SmartFan Fan3 tolerance */ |
|---|
| 189 | }; |
|---|
| 190 | |
|---|
| 191 | static const u8 W83792D_REG_POINTS[3][4] = { |
|---|
| 192 | { 0x85, /* SmartFanII: Fan1 temp point 1 */ |
|---|
| 193 | 0xE3, /* SmartFanII: Fan1 temp point 2 */ |
|---|
| 194 | 0xE4, /* SmartFanII: Fan1 temp point 3 */ |
|---|
| 195 | 0xE5 }, /* SmartFanII: Fan1 temp point 4 */ |
|---|
| 196 | { 0x86, /* SmartFanII: Fan2 temp point 1 */ |
|---|
| 197 | 0xE6, /* SmartFanII: Fan2 temp point 2 */ |
|---|
| 198 | 0xE7, /* SmartFanII: Fan2 temp point 3 */ |
|---|
| 199 | 0xE8 }, /* SmartFanII: Fan2 temp point 4 */ |
|---|
| 200 | { 0x96, /* SmartFanII: Fan3 temp point 1 */ |
|---|
| 201 | 0xE9, /* SmartFanII: Fan3 temp point 2 */ |
|---|
| 202 | 0xEA, /* SmartFanII: Fan3 temp point 3 */ |
|---|
| 203 | 0xEB } /* SmartFanII: Fan3 temp point 4 */ |
|---|
| 204 | }; |
|---|
| 205 | |
|---|
| 206 | static const u8 W83792D_REG_LEVELS[3][4] = { |
|---|
| 207 | { 0x88, /* (bit3-0) SmartFanII: Fan1 Non-Stop */ |
|---|
| 208 | 0x88, /* (bit7-4) SmartFanII: Fan1 Level 1 */ |
|---|
| 209 | 0xE0, /* (bit7-4) SmartFanII: Fan1 Level 2 */ |
|---|
| 210 | 0xE0 }, /* (bit3-0) SmartFanII: Fan1 Level 3 */ |
|---|
| 211 | { 0x89, /* (bit3-0) SmartFanII: Fan2 Non-Stop */ |
|---|
| 212 | 0x89, /* (bit7-4) SmartFanII: Fan2 Level 1 */ |
|---|
| 213 | 0xE1, /* (bit7-4) SmartFanII: Fan2 Level 2 */ |
|---|
| 214 | 0xE1 }, /* (bit3-0) SmartFanII: Fan2 Level 3 */ |
|---|
| 215 | { 0x98, /* (bit3-0) SmartFanII: Fan3 Non-Stop */ |
|---|
| 216 | 0x98, /* (bit7-4) SmartFanII: Fan3 Level 1 */ |
|---|
| 217 | 0xE2, /* (bit7-4) SmartFanII: Fan3 Level 2 */ |
|---|
| 218 | 0xE2 } /* (bit3-0) SmartFanII: Fan3 Level 3 */ |
|---|
| 219 | }; |
|---|
| 220 | |
|---|
| 221 | static inline u8 FAN_TO_REG(long rpm, int div) |
|---|
| 222 | { |
|---|
| 223 | if (rpm == 0) |
|---|
| 224 | return 255; |
|---|
| 225 | rpm = SENSORS_LIMIT(rpm, 1, 1000000); |
|---|
| 226 | return SENSORS_LIMIT(1350000/(rpm * div), 1, 254); |
|---|
| 227 | } |
|---|
| 228 | |
|---|
| 229 | #define IN_FROM_REG(nr,val) (((nr)<=1)?(val*2): \ |
|---|
| 230 | ((((nr)==6)||((nr)==7))?(val*6):(val*4))) |
|---|
| 231 | #define IN_TO_REG(nr,val) (((nr)<=1)?(val/2): \ |
|---|
| 232 | ((((nr)==6)||((nr)==7))?(val/6):(val/4))) |
|---|
| 233 | #define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) |
|---|
| 234 | #define TEMP_TO_REG(val) (SENSORS_LIMIT((val>=0)?((val)/10):((val)/10+256), 0, 255)) |
|---|
| 235 | #define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) |
|---|
| 236 | #define DIV_FROM_REG(val) (1 << (val)) |
|---|
| 237 | |
|---|
| 238 | #ifdef W83792D_DEBUG |
|---|
| 239 | #define ENTER() printk(KERN_DEBUG "w83792d: ENTERING %s, line: %d\n", __FUNCTION__, __LINE__); |
|---|
| 240 | #define LEAVE() printk(KERN_DEBUG "w83792d: LEAVING %s, line: %d\n", __FUNCTION__, __LINE__); |
|---|
| 241 | #else |
|---|
| 242 | #define ENTER() |
|---|
| 243 | #define LEAVE() |
|---|
| 244 | #endif |
|---|
| 245 | |
|---|
| 246 | struct w83792d_data { |
|---|
| 247 | struct i2c_client client; |
|---|
| 248 | struct semaphore lock; |
|---|
| 249 | int sysctl_id; |
|---|
| 250 | enum chips type; |
|---|
| 251 | |
|---|
| 252 | struct semaphore update_lock; |
|---|
| 253 | char valid; /* !=0 if following fields are valid */ |
|---|
| 254 | unsigned long last_updated; /* In jiffies */ |
|---|
| 255 | |
|---|
| 256 | struct i2c_client *lm75; /* for secondary I2C addresses */ |
|---|
| 257 | /* pointer to array of 2 subclients */ |
|---|
| 258 | |
|---|
| 259 | u8 in[9]; /* Register value */ |
|---|
| 260 | u8 in_max[9]; /* Register value */ |
|---|
| 261 | u8 in_min[9]; /* Register value */ |
|---|
| 262 | u16 low_bits; /* Register value */ |
|---|
| 263 | u8 has_fan; /* Bit vector */ |
|---|
| 264 | u8 fan[7]; /* Register value */ |
|---|
| 265 | u8 fan_min[7]; /* Register value */ |
|---|
| 266 | u8 fan_cfg; /* Configure Fan Mode */ |
|---|
| 267 | u8 temp1[3]; /* Register value */ |
|---|
| 268 | u8 temp_add[2][7]; /* Register value */ |
|---|
| 269 | u8 fan_div[7]; /* Fan Divisor */ |
|---|
| 270 | /*u8 vid; Register encoding, combined */ |
|---|
| 271 | u8 pwm[7]; /* We only consider the first 3 set of pwm, |
|---|
| 272 | although 792 chip has 7 set of pwm. */ |
|---|
| 273 | u8 pwm_flag[7]; /* indicates PWM or DC mode: 1->PWM; 0->DC */ |
|---|
| 274 | /* u8 vrm; VRM version */ |
|---|
| 275 | u32 alarms; /* realtime status register encoding,combined */ |
|---|
| 276 | u8 chassis[2]; /* [0]->Chassis status, [1]->CLR_CHS */ |
|---|
| 277 | u8 thermal_cruise[3]; /* Smart FanI: Fan1,2,3 target value */ |
|---|
| 278 | u8 fan_tolerance[3]; /* Fan1,2,3 tolerance(Smart Fan I/II) */ |
|---|
| 279 | u8 sf2_points[3][4]; /* Smart FanII: Fan1,2,3 temperature points */ |
|---|
| 280 | u8 sf2_levels[3][4]; /* Smart FanII: Fan1,2,3 duty cycle levels */ |
|---|
| 281 | }; |
|---|
| 282 | |
|---|
| 283 | |
|---|
| 284 | static int w83792d_attach_adapter(struct i2c_adapter *adapter); |
|---|
| 285 | static int w83792d_detect(struct i2c_adapter *adapter, int address, |
|---|
| 286 | unsigned short flags, int kind); |
|---|
| 287 | static int w83792d_detach_client(struct i2c_client *client); |
|---|
| 288 | |
|---|
| 289 | static inline int w83792d_read_value(struct i2c_client *client, u8 reg); |
|---|
| 290 | static inline int w83792d_write_value(struct i2c_client *client, u8 reg, |
|---|
| 291 | u8 value); |
|---|
| 292 | static void w83792d_init_client(struct i2c_client *client); |
|---|
| 293 | static void w83792d_update_client(struct i2c_client *client); |
|---|
| 294 | #ifdef W83792D_DEBUG |
|---|
| 295 | static void w83792d_print_debug(struct w83792d_data *data); |
|---|
| 296 | #endif |
|---|
| 297 | static void w83792d_in(struct i2c_client *client, int operation, |
|---|
| 298 | int ctl_name, int *nrels_mag, long *results); |
|---|
| 299 | static void w83792d_fan(struct i2c_client *client, int operation, |
|---|
| 300 | int ctl_name, int *nrels_mag, long *results); |
|---|
| 301 | static void w83792d_temp(struct i2c_client *client, int operation, |
|---|
| 302 | int ctl_name, int *nrels_mag, long *results); |
|---|
| 303 | static void w83792d_temp_add(struct i2c_client *client, int operation, |
|---|
| 304 | int ctl_name, int *nrels_mag, long *results); |
|---|
| 305 | /*static void w83792d_vid(struct i2c_client *client, int operation, |
|---|
| 306 | int ctl_name, int *nrels_mag, long *results); |
|---|
| 307 | static void w83792d_vrm(struct i2c_client *client, int operation, |
|---|
| 308 | int ctl_name, int *nrels_mag, long *results); */ |
|---|
| 309 | static void w83792d_fan_div(struct i2c_client *client, int operation, |
|---|
| 310 | int ctl_name, int *nrels_mag, long *results); |
|---|
| 311 | static void w83792d_alarms(struct i2c_client *client, int operation, |
|---|
| 312 | int ctl_name, int *nrels_mag, long *results); |
|---|
| 313 | static void w83792d_chassis(struct i2c_client *client, int operation, |
|---|
| 314 | int ctl_name, int *nrels_mag, long *results); |
|---|
| 315 | static void w83792d_pwm(struct i2c_client *client, int operation, |
|---|
| 316 | int ctl_name, int *nrels_mag, long *results); |
|---|
| 317 | static void w83792d_pwm_flag(struct i2c_client *client, int operation, |
|---|
| 318 | int ctl_name, int *nrels_mag, long *results); |
|---|
| 319 | static void w83792d_fan_cfg(struct i2c_client *client, int operation, |
|---|
| 320 | int ctl_name, int *nrels_mag, long *results); |
|---|
| 321 | static void w83792d_thermal_cruise(struct i2c_client *client, int operation, |
|---|
| 322 | int ctl_name, int *nrels_mag, long *results); |
|---|
| 323 | static void w83792d_fan_tolerance(struct i2c_client *client, int operation, |
|---|
| 324 | int ctl_name, int *nrels_mag, long *results); |
|---|
| 325 | static void w83792d_sf2_points(struct i2c_client *client, int operation, |
|---|
| 326 | int ctl_name, int *nrels_mag, long *results); |
|---|
| 327 | static void w83792d_sf2_levels(struct i2c_client *client, int operation, |
|---|
| 328 | int ctl_name, int *nrels_mag, long *results); |
|---|
| 329 | |
|---|
| 330 | static struct i2c_driver w83792d_driver = { |
|---|
| 331 | .name = "W83792D sensor driver", |
|---|
| 332 | .flags = I2C_DF_NOTIFY, |
|---|
| 333 | .attach_adapter = w83792d_attach_adapter, |
|---|
| 334 | .detach_client = w83792d_detach_client, |
|---|
| 335 | }; |
|---|
| 336 | |
|---|
| 337 | /* The /proc/sys entries */ |
|---|
| 338 | /* -- SENSORS SYSCTL START -- */ |
|---|
| 339 | |
|---|
| 340 | #define W83792D_SYSCTL_IN0 1000 |
|---|
| 341 | #define W83792D_SYSCTL_IN1 1001 |
|---|
| 342 | #define W83792D_SYSCTL_IN2 1002 |
|---|
| 343 | #define W83792D_SYSCTL_IN3 1003 |
|---|
| 344 | #define W83792D_SYSCTL_IN4 1004 |
|---|
| 345 | #define W83792D_SYSCTL_IN5 1005 |
|---|
| 346 | #define W83792D_SYSCTL_IN6 1006 |
|---|
| 347 | #define W83792D_SYSCTL_IN7 1007 |
|---|
| 348 | #define W83792D_SYSCTL_IN8 1008 |
|---|
| 349 | #define W83792D_SYSCTL_FAN1 1101 |
|---|
| 350 | #define W83792D_SYSCTL_FAN2 1102 |
|---|
| 351 | #define W83792D_SYSCTL_FAN3 1103 |
|---|
| 352 | #define W83792D_SYSCTL_FAN4 1104 |
|---|
| 353 | #define W83792D_SYSCTL_FAN5 1105 |
|---|
| 354 | #define W83792D_SYSCTL_FAN6 1106 |
|---|
| 355 | #define W83792D_SYSCTL_FAN7 1107 |
|---|
| 356 | |
|---|
| 357 | #define W83792D_SYSCTL_TEMP1 1200 |
|---|
| 358 | #define W83792D_SYSCTL_TEMP2 1201 |
|---|
| 359 | #define W83792D_SYSCTL_TEMP3 1202 |
|---|
| 360 | /*#define W83792D_SYSCTL_VID 1300 |
|---|
| 361 | #define W83792D_SYSCTL_VRM 1301*/ |
|---|
| 362 | #define W83792D_SYSCTL_PWM_FLAG 1400 |
|---|
| 363 | #define W83792D_SYSCTL_PWM1 1401 |
|---|
| 364 | #define W83792D_SYSCTL_PWM2 1402 |
|---|
| 365 | #define W83792D_SYSCTL_PWM3 1403 |
|---|
| 366 | #define W83792D_SYSCTL_FAN_CFG 1500 /* control Fan Mode */ |
|---|
| 367 | #define W83792D_SYSCTL_FAN_DIV 1501 |
|---|
| 368 | #define W83792D_SYSCTL_CHASSIS 1502 /* control Case Open */ |
|---|
| 369 | #define W83792D_SYSCTL_ALARMS 1503 |
|---|
| 370 | |
|---|
| 371 | #define W83792D_SYSCTL_THERMAL_CRUISE 1600 /* Smart Fan I: target value */ |
|---|
| 372 | #define W83792D_SYSCTL_FAN_TOLERANCE 1601 /* Smart Fan I/II: tolerance */ |
|---|
| 373 | #define W83792D_SYSCTL_SF2_POINTS_FAN1 1602 /* Smart Fan II: Fan1 points */ |
|---|
| 374 | #define W83792D_SYSCTL_SF2_POINTS_FAN2 1603 /* Smart Fan II: Fan2 points */ |
|---|
| 375 | #define W83792D_SYSCTL_SF2_POINTS_FAN3 1604 /* Smart Fan II: Fan3 points */ |
|---|
| 376 | #define W83792D_SYSCTL_SF2_LEVELS_FAN1 1605 /* Smart Fan II: Fan1 levels */ |
|---|
| 377 | #define W83792D_SYSCTL_SF2_LEVELS_FAN2 1606 /* Smart Fan II: Fan2 levels */ |
|---|
| 378 | #define W83792D_SYSCTL_SF2_LEVELS_FAN3 1607 /* Smart Fan II: Fan3 levels */ |
|---|
| 379 | |
|---|
| 380 | #define W83792D_ALARM_IN0 0x0001 |
|---|
| 381 | #define W83792D_ALARM_IN1 0x0002 |
|---|
| 382 | #define W83792D_ALARM_IN2 0x0100 |
|---|
| 383 | #define W83792D_ALARM_IN3 0x0200 |
|---|
| 384 | #define W83792D_ALARM_IN4 0x0400 |
|---|
| 385 | #define W83792D_ALARM_IN5 0x0800 |
|---|
| 386 | #define W83792D_ALARM_IN6 0x1000 |
|---|
| 387 | #define W83792D_ALARM_IN7 0x80000 |
|---|
| 388 | #define W83792D_ALARM_IN8 0x100000 |
|---|
| 389 | #define W83792D_ALARM_TEMP1 0x0004 |
|---|
| 390 | #define W83792D_ALARM_TEMP2 0x0008 |
|---|
| 391 | #define W83792D_ALARM_TEMP3 0x0010 |
|---|
| 392 | #define W83792D_ALARM_FAN1 0x0020 |
|---|
| 393 | #define W83792D_ALARM_FAN2 0x0040 |
|---|
| 394 | #define W83792D_ALARM_FAN3 0x0080 |
|---|
| 395 | #define W83792D_ALARM_FAN4 0x200000 |
|---|
| 396 | #define W83792D_ALARM_FAN5 0x400000 |
|---|
| 397 | #define W83792D_ALARM_FAN6 0x800000 |
|---|
| 398 | #define W83792D_ALARM_FAN7 0x8000 |
|---|
| 399 | |
|---|
| 400 | /* -- SENSORS SYSCTL END -- */ |
|---|
| 401 | |
|---|
| 402 | /* These files are created for detected chip, |
|---|
| 403 | W83792D has 9 voltages 7 fans and 3 temperatures. */ |
|---|
| 404 | static ctl_table w83792d_dir_table_template[] = |
|---|
| 405 | { |
|---|
| 406 | {W83792D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 407 | &i2c_sysctl_real, NULL, &w83792d_in}, |
|---|
| 408 | {W83792D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 409 | &i2c_sysctl_real, NULL, &w83792d_in}, |
|---|
| 410 | {W83792D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 411 | &i2c_sysctl_real, NULL, &w83792d_in}, |
|---|
| 412 | {W83792D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 413 | &i2c_sysctl_real, NULL, &w83792d_in}, |
|---|
| 414 | {W83792D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 415 | &i2c_sysctl_real, NULL, &w83792d_in}, |
|---|
| 416 | {W83792D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 417 | &i2c_sysctl_real, NULL, &w83792d_in}, |
|---|
| 418 | {W83792D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 419 | &i2c_sysctl_real, NULL, &w83792d_in}, |
|---|
| 420 | {W83792D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 421 | &i2c_sysctl_real, NULL, &w83792d_in}, |
|---|
| 422 | {W83792D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 423 | &i2c_sysctl_real, NULL, &w83792d_in}, |
|---|
| 424 | {W83792D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 425 | &i2c_sysctl_real, NULL, &w83792d_fan}, |
|---|
| 426 | {W83792D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 427 | &i2c_sysctl_real, NULL, &w83792d_fan}, |
|---|
| 428 | {W83792D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 429 | &i2c_sysctl_real, NULL, &w83792d_fan}, |
|---|
| 430 | {W83792D_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 431 | &i2c_sysctl_real, NULL, &w83792d_fan}, |
|---|
| 432 | {W83792D_SYSCTL_FAN5, "fan5", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 433 | &i2c_sysctl_real, NULL, &w83792d_fan}, |
|---|
| 434 | {W83792D_SYSCTL_FAN6, "fan6", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 435 | &i2c_sysctl_real, NULL, &w83792d_fan}, |
|---|
| 436 | {W83792D_SYSCTL_FAN7, "fan7", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 437 | &i2c_sysctl_real, NULL, &w83792d_fan}, |
|---|
| 438 | {W83792D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 439 | &i2c_sysctl_real, NULL, &w83792d_temp}, |
|---|
| 440 | {W83792D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 441 | &i2c_sysctl_real, NULL, &w83792d_temp_add}, |
|---|
| 442 | {W83792D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 443 | &i2c_sysctl_real, NULL, &w83792d_temp_add}, |
|---|
| 444 | /*{W83792D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, |
|---|
| 445 | &i2c_sysctl_real, NULL, &w83792d_vid}, */ |
|---|
| 446 | {W83792D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 447 | &i2c_sysctl_real, NULL, &w83792d_fan_div}, |
|---|
| 448 | {W83792D_SYSCTL_ALARMS, "alarms", NULL, 0, 0644, NULL, |
|---|
| 449 | &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_alarms}, |
|---|
| 450 | {W83792D_SYSCTL_CHASSIS, "chassis", NULL, 0, 0644, NULL, |
|---|
| 451 | &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_chassis}, |
|---|
| 452 | {W83792D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 453 | &i2c_sysctl_real, NULL, &w83792d_pwm}, |
|---|
| 454 | {W83792D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 455 | &i2c_sysctl_real, NULL, &w83792d_pwm}, |
|---|
| 456 | {W83792D_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 457 | &i2c_sysctl_real, NULL, &w83792d_pwm}, |
|---|
| 458 | {W83792D_SYSCTL_PWM_FLAG, "pwm_flag", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 459 | &i2c_sysctl_real, NULL, &w83792d_pwm_flag}, |
|---|
| 460 | {W83792D_SYSCTL_FAN_CFG, "fan_cfg", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 461 | &i2c_sysctl_real, NULL, &w83792d_fan_cfg}, |
|---|
| 462 | /*{W83792D_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real, |
|---|
| 463 | &i2c_sysctl_real, NULL, &w83792d_vrm}, */ |
|---|
| 464 | {W83792D_SYSCTL_THERMAL_CRUISE, "thermal_cruise", NULL, 0, 0644, NULL, |
|---|
| 465 | &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_thermal_cruise}, |
|---|
| 466 | {W83792D_SYSCTL_FAN_TOLERANCE, "fan_tolerance", NULL, 0, 0644, |
|---|
| 467 | NULL, &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_fan_tolerance}, |
|---|
| 468 | {W83792D_SYSCTL_SF2_POINTS_FAN1, "sf2_points_fan1", NULL, 0, 0644, NULL, |
|---|
| 469 | &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_sf2_points}, |
|---|
| 470 | {W83792D_SYSCTL_SF2_POINTS_FAN2, "sf2_points_fan2", NULL, 0, 0644, NULL, |
|---|
| 471 | &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_sf2_points}, |
|---|
| 472 | {W83792D_SYSCTL_SF2_POINTS_FAN3, "sf2_points_fan3", NULL, 0, 0644, NULL, |
|---|
| 473 | &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_sf2_points}, |
|---|
| 474 | {W83792D_SYSCTL_SF2_LEVELS_FAN1, "sf2_levels_fan1", NULL, 0, 0644, NULL, |
|---|
| 475 | &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_sf2_levels}, |
|---|
| 476 | {W83792D_SYSCTL_SF2_LEVELS_FAN2, "sf2_levels_fan2", NULL, 0, 0644, NULL, |
|---|
| 477 | &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_sf2_levels}, |
|---|
| 478 | {W83792D_SYSCTL_SF2_LEVELS_FAN3, "sf2_levels_fan3", NULL, 0, 0644, NULL, |
|---|
| 479 | &i2c_proc_real, &i2c_sysctl_real, NULL, &w83792d_sf2_levels}, |
|---|
| 480 | {0} |
|---|
| 481 | }; |
|---|
| 482 | |
|---|
| 483 | /* This function is called when: |
|---|
| 484 | * w83792d_driver is inserted (when this module is loaded), for each |
|---|
| 485 | available adapter |
|---|
| 486 | * when a new adapter is inserted (and w83792d_driver is still present) */ |
|---|
| 487 | static int w83792d_attach_adapter(struct i2c_adapter *adapter) |
|---|
| 488 | { |
|---|
| 489 | int i_tmp; |
|---|
| 490 | ENTER() |
|---|
| 491 | i_tmp = i2c_detect(adapter, &addr_data, w83792d_detect); |
|---|
| 492 | LEAVE() |
|---|
| 493 | return i_tmp; |
|---|
| 494 | } |
|---|
| 495 | |
|---|
| 496 | static int w83792d_detect(struct i2c_adapter *adapter, int address, |
|---|
| 497 | unsigned short flags, int kind) |
|---|
| 498 | { |
|---|
| 499 | int i, val1 = 0, val2 = 0, id; |
|---|
| 500 | struct i2c_client *new_client; |
|---|
| 501 | struct w83792d_data *data; |
|---|
| 502 | int err = 0; |
|---|
| 503 | const char *type_name = ""; |
|---|
| 504 | const char *client_name = ""; |
|---|
| 505 | |
|---|
| 506 | ENTER() |
|---|
| 507 | |
|---|
| 508 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { |
|---|
| 509 | LEAVE() |
|---|
| 510 | goto ERROR0; |
|---|
| 511 | } |
|---|
| 512 | |
|---|
| 513 | /* OK. For now, we presume we have a valid client. We now create the |
|---|
| 514 | client structure, even though we cannot fill it completely yet. |
|---|
| 515 | But it allows us to access w83792d_{read,write}_value. */ |
|---|
| 516 | |
|---|
| 517 | if (!(data = kmalloc(sizeof(struct w83792d_data), GFP_KERNEL))) { |
|---|
| 518 | printk(KERN_ERR "w83792d: Out of memory in w83792d_detect (new_client).\n"); |
|---|
| 519 | err = -ENOMEM; |
|---|
| 520 | LEAVE() |
|---|
| 521 | goto ERROR0; |
|---|
| 522 | } |
|---|
| 523 | |
|---|
| 524 | new_client = &data->client; |
|---|
| 525 | new_client->addr = address; |
|---|
| 526 | new_client->data = data; |
|---|
| 527 | new_client->adapter = adapter; |
|---|
| 528 | new_client->driver = &w83792d_driver; |
|---|
| 529 | new_client->flags = 0; |
|---|
| 530 | |
|---|
| 531 | /* Now, we do the remaining detection. */ |
|---|
| 532 | if (kind < 0) { |
|---|
| 533 | if (w83792d_read_value(new_client, W83792D_REG_CONFIG)&0x80) { |
|---|
| 534 | LEAVE() |
|---|
| 535 | goto ERROR1; |
|---|
| 536 | } |
|---|
| 537 | val1 = w83792d_read_value(new_client, W83792D_REG_BANK); |
|---|
| 538 | val2 = w83792d_read_value(new_client, W83792D_REG_CHIPMAN); |
|---|
| 539 | #ifdef W83792D_DEBUG |
|---|
| 540 | printk(KERN_DEBUG "w83792d: val1 is: %d, val2 is: %d\n", |
|---|
| 541 | val1, val2); |
|---|
| 542 | #endif |
|---|
| 543 | /* Check for Winbond ID if in bank 0 */ |
|---|
| 544 | if (!(val1 & 0x07)) { /* is Bank0 */ |
|---|
| 545 | if (((!(val1 & 0x80)) && (val2 != 0xa3)) || |
|---|
| 546 | ((val1 & 0x80) && (val2 != 0x5c))) { |
|---|
| 547 | LEAVE() |
|---|
| 548 | goto ERROR1; |
|---|
| 549 | } |
|---|
| 550 | } |
|---|
| 551 | /* check address at 0x48. */ |
|---|
| 552 | if (w83792d_read_value(new_client, W83792D_REG_I2C_ADDR) |
|---|
| 553 | != address) { |
|---|
| 554 | LEAVE() |
|---|
| 555 | goto ERROR1; |
|---|
| 556 | } |
|---|
| 557 | } |
|---|
| 558 | |
|---|
| 559 | /* We have either had a force parameter, or we have already detected |
|---|
| 560 | the Winbond. Put it now into bank 0 and Vendor ID High Byte */ |
|---|
| 561 | w83792d_write_value(new_client, W83792D_REG_BANK, |
|---|
| 562 | (w83792d_read_value(new_client, |
|---|
| 563 | W83792D_REG_BANK) & 0x78) | |
|---|
| 564 | 0x80); |
|---|
| 565 | |
|---|
| 566 | /* Determine the chip type. */ |
|---|
| 567 | if (kind <= 0) { |
|---|
| 568 | /* get vendor ID */ |
|---|
| 569 | val2 = w83792d_read_value(new_client, W83792D_REG_CHIPMAN); |
|---|
| 570 | if (val2 != 0x5c) { /* the vendor is NOT Winbond */ |
|---|
| 571 | LEAVE() |
|---|
| 572 | goto ERROR1; |
|---|
| 573 | } |
|---|
| 574 | val1 = w83792d_read_value(new_client, W83792D_REG_WCHIPID); |
|---|
| 575 | if (val1 == 0x7a) { |
|---|
| 576 | kind = w83792d; |
|---|
| 577 | } else { |
|---|
| 578 | if (kind == 0) |
|---|
| 579 | printk(KERN_WARNING "w83792d: Ignoring " |
|---|
| 580 | "'force' parameter for unknown chip " |
|---|
| 581 | "at adapter %d, address 0x%02x\n", |
|---|
| 582 | i2c_adapter_id(adapter), address); |
|---|
| 583 | LEAVE() |
|---|
| 584 | goto ERROR1; |
|---|
| 585 | } |
|---|
| 586 | } |
|---|
| 587 | |
|---|
| 588 | if (kind == w83792d) { |
|---|
| 589 | type_name = "w83792d"; |
|---|
| 590 | client_name = "W83792D chip"; |
|---|
| 591 | } else { |
|---|
| 592 | printk(KERN_ERR "w83792d: Internal error: unknown kind (%d)?!?", |
|---|
| 593 | kind); |
|---|
| 594 | LEAVE() |
|---|
| 595 | goto ERROR1; |
|---|
| 596 | } |
|---|
| 597 | |
|---|
| 598 | /* Fill in the remaining client fields and put it into the global list */ |
|---|
| 599 | strcpy(new_client->name, client_name); |
|---|
| 600 | data->type = kind; |
|---|
| 601 | data->valid = 0; |
|---|
| 602 | data->has_fan = 0x07; /* at least 3 fan inputs */ |
|---|
| 603 | init_MUTEX(&data->update_lock); |
|---|
| 604 | |
|---|
| 605 | /* Tell the I2C layer a new client has arrived */ |
|---|
| 606 | if ((err = i2c_attach_client(new_client))) { |
|---|
| 607 | LEAVE() |
|---|
| 608 | goto ERROR1; |
|---|
| 609 | } |
|---|
| 610 | |
|---|
| 611 | /* attach secondary i2c lm75-like clients */ |
|---|
| 612 | if (!(data->lm75 = kmalloc(2 * sizeof(struct i2c_client), |
|---|
| 613 | GFP_KERNEL))) { |
|---|
| 614 | err = -ENOMEM; |
|---|
| 615 | goto ERROR2; |
|---|
| 616 | } |
|---|
| 617 | id = i2c_adapter_id(adapter); |
|---|
| 618 | if(force_subclients[0] == id && force_subclients[1] == address) { |
|---|
| 619 | if(force_subclients[2] < 0x48 || force_subclients[2] > 0x4b) { |
|---|
| 620 | printk(KERN_ERR "w83792d.o: Invalid subclient address %d; must be 0x48-0x4b\n", |
|---|
| 621 | force_subclients[2]); |
|---|
| 622 | goto ERROR5; |
|---|
| 623 | } |
|---|
| 624 | if(force_subclients[3] < 0x4c || force_subclients[3] > 0x4f) { |
|---|
| 625 | printk(KERN_ERR "w83792d.o: Invalid subclient address %d; must be 0x4c-0x4f\n", |
|---|
| 626 | force_subclients[3]); |
|---|
| 627 | goto ERROR5; |
|---|
| 628 | } |
|---|
| 629 | w83792d_write_value(new_client, |
|---|
| 630 | W83792D_REG_I2C_SUBADDR, |
|---|
| 631 | 0x40 | (force_subclients[2] & 0x03) | |
|---|
| 632 | ((force_subclients[3] & 0x03) <<4)); |
|---|
| 633 | data->lm75[0].addr = force_subclients[2]; |
|---|
| 634 | data->lm75[1].addr = force_subclients[3]; |
|---|
| 635 | } else { |
|---|
| 636 | val1 = w83792d_read_value(new_client, |
|---|
| 637 | W83792D_REG_I2C_SUBADDR); |
|---|
| 638 | data->lm75[0].addr = 0x48 + (val1 & 0x07); |
|---|
| 639 | data->lm75[1].addr = 0x48 + ((val1 >> 4) & 0x07); |
|---|
| 640 | if (data->lm75[0].addr == data->lm75[1].addr) |
|---|
| 641 | printk(KERN_WARNING "w83792d: Subclients have the same " |
|---|
| 642 | "address (0x%02x)! Use force_subclients.\n", |
|---|
| 643 | data->lm75[0].addr); |
|---|
| 644 | } |
|---|
| 645 | client_name = "W83792D subclient"; |
|---|
| 646 | |
|---|
| 647 | |
|---|
| 648 | for (i = 0; i <= 1; i++) { |
|---|
| 649 | data->lm75[i].data = NULL; /* store all data in w83792d */ |
|---|
| 650 | data->lm75[i].adapter = adapter; |
|---|
| 651 | data->lm75[i].driver = &w83792d_driver; |
|---|
| 652 | data->lm75[i].flags = 0; |
|---|
| 653 | strcpy(data->lm75[i].name, client_name); |
|---|
| 654 | if ((err = i2c_attach_client(&(data->lm75[i])))) { |
|---|
| 655 | printk(KERN_ERR "w83792d.o: Subclient %d registration at address 0x%x failed.\n", |
|---|
| 656 | i, data->lm75[i].addr); |
|---|
| 657 | if (i == 1) |
|---|
| 658 | goto ERROR6; |
|---|
| 659 | goto ERROR5; |
|---|
| 660 | } |
|---|
| 661 | } |
|---|
| 662 | |
|---|
| 663 | /* Read GPIO enable register to check if pins for fan 4,5 are used as |
|---|
| 664 | GPIO */ |
|---|
| 665 | val1 = w83792d_read_value(new_client, W83792D_REG_GPIO_EN); |
|---|
| 666 | if (!(val1 & 0x40)) |
|---|
| 667 | data->has_fan |= 0x08; /* fan 4 */ |
|---|
| 668 | if (!(val1 & 0x20)) |
|---|
| 669 | data->has_fan |= 0x10; /* fan 5 */ |
|---|
| 670 | |
|---|
| 671 | val1 = w83792d_read_value(new_client, W83792D_REG_PIN); |
|---|
| 672 | if (val1 & 0x40) |
|---|
| 673 | data->has_fan |= 0x20; /* fan 6 */ |
|---|
| 674 | if (val1 & 0x04) |
|---|
| 675 | data->has_fan |= 0x40; /* fan 7 */ |
|---|
| 676 | |
|---|
| 677 | /* Register a new directory entry with module sensors */ |
|---|
| 678 | if ((i = i2c_register_entry(new_client, type_name, |
|---|
| 679 | w83792d_dir_table_template, THIS_MODULE)) < 0) { |
|---|
| 680 | err = i; |
|---|
| 681 | goto ERROR7; |
|---|
| 682 | } |
|---|
| 683 | data->sysctl_id = i; |
|---|
| 684 | |
|---|
| 685 | /* Initialize the chip */ |
|---|
| 686 | w83792d_init_client(new_client); |
|---|
| 687 | LEAVE() |
|---|
| 688 | return 0; |
|---|
| 689 | |
|---|
| 690 | ERROR7: |
|---|
| 691 | i2c_detach_client(& |
|---|
| 692 | (((struct |
|---|
| 693 | w83792d_data *) (new_client->data))-> |
|---|
| 694 | lm75[1])); |
|---|
| 695 | ERROR6: |
|---|
| 696 | i2c_detach_client(& |
|---|
| 697 | (((struct |
|---|
| 698 | w83792d_data *) (new_client->data))-> |
|---|
| 699 | lm75[0])); |
|---|
| 700 | ERROR5: |
|---|
| 701 | kfree(((struct w83792d_data *) (new_client->data))->lm75); |
|---|
| 702 | ERROR2: |
|---|
| 703 | i2c_detach_client(new_client); |
|---|
| 704 | ERROR1: |
|---|
| 705 | kfree(data); |
|---|
| 706 | ERROR0: |
|---|
| 707 | |
|---|
| 708 | LEAVE() |
|---|
| 709 | return err; |
|---|
| 710 | } |
|---|
| 711 | |
|---|
| 712 | static int w83792d_detach_client(struct i2c_client *client) |
|---|
| 713 | { |
|---|
| 714 | int err; |
|---|
| 715 | struct w83792d_data *data = client->data; |
|---|
| 716 | ENTER() |
|---|
| 717 | |
|---|
| 718 | i2c_deregister_entry(data->sysctl_id); |
|---|
| 719 | |
|---|
| 720 | if ((err = i2c_detach_client(client))) { |
|---|
| 721 | printk(KERN_ERR "w83792d: Client deregistration failed, client not detached.\n"); |
|---|
| 722 | LEAVE() |
|---|
| 723 | return err; |
|---|
| 724 | } |
|---|
| 725 | i2c_detach_client(&(data->lm75[0])); |
|---|
| 726 | i2c_detach_client(&(data->lm75[1])); |
|---|
| 727 | kfree(data->lm75); |
|---|
| 728 | kfree(data); |
|---|
| 729 | |
|---|
| 730 | LEAVE() |
|---|
| 731 | return 0; |
|---|
| 732 | } |
|---|
| 733 | |
|---|
| 734 | /* Read the w83792d register value, only use bank 0 of the 792 chip */ |
|---|
| 735 | static inline int |
|---|
| 736 | w83792d_read_value(struct i2c_client *client, u8 reg) |
|---|
| 737 | { |
|---|
| 738 | return i2c_smbus_read_byte_data(client, reg); |
|---|
| 739 | } |
|---|
| 740 | |
|---|
| 741 | /* Write value into the w83792d registers, only use bank 0 of the 792 chip */ |
|---|
| 742 | static inline int |
|---|
| 743 | w83792d_write_value(struct i2c_client *client, u8 reg, u8 value) |
|---|
| 744 | { |
|---|
| 745 | return i2c_smbus_write_byte_data(client, reg, value); |
|---|
| 746 | } |
|---|
| 747 | |
|---|
| 748 | /* Called when we have found a new W83792D. */ |
|---|
| 749 | static void w83792d_init_client(struct i2c_client *client) |
|---|
| 750 | { |
|---|
| 751 | int temp2_cfg, temp3_cfg; |
|---|
| 752 | u8 vid_in_b; |
|---|
| 753 | |
|---|
| 754 | ENTER() |
|---|
| 755 | |
|---|
| 756 | if (init) { |
|---|
| 757 | w83792d_write_value(client, W83792D_REG_CONFIG, 0x80); |
|---|
| 758 | } |
|---|
| 759 | /* data->vrm = 90; */ /* maybe need to be modified! */ |
|---|
| 760 | |
|---|
| 761 | /* Clear the bit6 of W83792D_REG_VID_IN_B(set it into 0): |
|---|
| 762 | W83792D_REG_VID_IN_B bit6 = 0: the high/low limit of |
|---|
| 763 | vin0/vin1 can be modified by user; |
|---|
| 764 | W83792D_REG_VID_IN_B bit6 = 1: the high/low limit of |
|---|
| 765 | vin0/vin1 auto-updated, can NOT be modified by user. */ |
|---|
| 766 | vid_in_b = w83792d_read_value(client, W83792D_REG_VID_IN_B); |
|---|
| 767 | w83792d_write_value(client, W83792D_REG_VID_IN_B, |
|---|
| 768 | vid_in_b & 0xbf); |
|---|
| 769 | |
|---|
| 770 | temp2_cfg = w83792d_read_value(client, W83792D_REG_TEMP_ADD[0][6]); |
|---|
| 771 | temp3_cfg = w83792d_read_value(client, W83792D_REG_TEMP_ADD[1][6]); |
|---|
| 772 | w83792d_write_value(client, W83792D_REG_TEMP_ADD[0][6], |
|---|
| 773 | temp2_cfg & 0xe6); |
|---|
| 774 | w83792d_write_value(client, W83792D_REG_TEMP_ADD[1][6], |
|---|
| 775 | temp3_cfg & 0xe6); |
|---|
| 776 | |
|---|
| 777 | /* Start monitoring */ |
|---|
| 778 | w83792d_write_value(client, W83792D_REG_CONFIG, (w83792d_read_value( |
|---|
| 779 | client, W83792D_REG_CONFIG) & 0xf7) | 0x01); |
|---|
| 780 | LEAVE() |
|---|
| 781 | } |
|---|
| 782 | |
|---|
| 783 | static void w83792d_update_client(struct i2c_client *client) |
|---|
| 784 | { |
|---|
| 785 | struct w83792d_data *data = client->data; |
|---|
| 786 | int i, j; |
|---|
| 787 | u8 reg_array_tmp[4], pwm_array_tmp[7], reg_tmp; |
|---|
| 788 | |
|---|
| 789 | down(&data->update_lock); |
|---|
| 790 | |
|---|
| 791 | if (time_after(jiffies - data->last_updated, HZ * 3) || |
|---|
| 792 | time_before(jiffies, data->last_updated) || !data->valid) { |
|---|
| 793 | pr_debug(KERN_DEBUG "Starting device update\n"); |
|---|
| 794 | |
|---|
| 795 | /* Update the voltages measured value and limits */ |
|---|
| 796 | for (i = 0; i < 9; i++) { |
|---|
| 797 | data->in[i] = w83792d_read_value(client, |
|---|
| 798 | W83792D_REG_IN[i]); |
|---|
| 799 | data->in_max[i] = w83792d_read_value(client, |
|---|
| 800 | W83792D_REG_IN_MAX[i]); |
|---|
| 801 | data->in_min[i] = w83792d_read_value(client, |
|---|
| 802 | W83792D_REG_IN_MIN[i]); |
|---|
| 803 | } |
|---|
| 804 | data->low_bits = w83792d_read_value(client, |
|---|
| 805 | W83792D_REG_LOW_BITS1) + |
|---|
| 806 | (w83792d_read_value(client, |
|---|
| 807 | W83792D_REG_LOW_BITS2) << 8); |
|---|
| 808 | |
|---|
| 809 | for (i = 0; i < 7; i++) { |
|---|
| 810 | /* Update the Fan measured value and limits */ |
|---|
| 811 | data->fan[i] = w83792d_read_value(client, |
|---|
| 812 | W83792D_REG_FAN[i]); |
|---|
| 813 | data->fan_min[i] = w83792d_read_value(client, |
|---|
| 814 | W83792D_REG_FAN_MIN[i]); |
|---|
| 815 | /* Update the PWM/DC Value and PWM/DC flag */ |
|---|
| 816 | pwm_array_tmp[i] = w83792d_read_value(client, |
|---|
| 817 | W83792D_REG_PWM[i]); |
|---|
| 818 | data->pwm[i] = pwm_array_tmp[i] & 0x0f; |
|---|
| 819 | data->pwm_flag[i] = pwm_array_tmp[i] >> 7; |
|---|
| 820 | } |
|---|
| 821 | data->fan_cfg = w83792d_read_value(client, W83792D_REG_FAN_CFG); |
|---|
| 822 | |
|---|
| 823 | /* Update the Fan Divisor */ |
|---|
| 824 | for (i = 0; i < 4; i++) { |
|---|
| 825 | reg_array_tmp[i] = w83792d_read_value(client, W83792D_REG_FAN_DIV[i]); |
|---|
| 826 | } |
|---|
| 827 | data->fan_div[0] = reg_array_tmp[0] & 0x07; |
|---|
| 828 | data->fan_div[1] = (reg_array_tmp[0] >> 4) & 0x07; |
|---|
| 829 | data->fan_div[2] = reg_array_tmp[1] & 0x07; |
|---|
| 830 | data->fan_div[3] = (reg_array_tmp[1] >> 4) & 0x07; |
|---|
| 831 | data->fan_div[4] = reg_array_tmp[2] & 0x07; |
|---|
| 832 | data->fan_div[5] = (reg_array_tmp[2] >> 4) & 0x07; |
|---|
| 833 | data->fan_div[6] = reg_array_tmp[3] & 0x07; |
|---|
| 834 | |
|---|
| 835 | /* Update the Temperature1 measured value and limits */ |
|---|
| 836 | data->temp1[0] = w83792d_read_value(client, W83792D_REG_TEMP1); |
|---|
| 837 | data->temp1[1] = w83792d_read_value(client, W83792D_REG_TEMP1_OVER); |
|---|
| 838 | data->temp1[2] = w83792d_read_value(client, W83792D_REG_TEMP1_HYST); |
|---|
| 839 | |
|---|
| 840 | /* Update the Temperature2/3 measured value and limits */ |
|---|
| 841 | for (i = 0; i < 7; i++) { |
|---|
| 842 | data->temp_add[0][i] = w83792d_read_value(client, |
|---|
| 843 | W83792D_REG_TEMP_ADD[0][i]); |
|---|
| 844 | data->temp_add[1][i] = w83792d_read_value(client, |
|---|
| 845 | W83792D_REG_TEMP_ADD[1][i]); |
|---|
| 846 | } |
|---|
| 847 | |
|---|
| 848 | /* Update the VID */ |
|---|
| 849 | /* i = w83792d_read_value(client, W83792D_REG_FAN_DIV[0]); |
|---|
| 850 | data->vid = i & 0x0f; |
|---|
| 851 | data->vid |= |
|---|
| 852 | (w83792d_read_value(client, W83792D_REG_CHIPID) & 0x01) |
|---|
| 853 | << 4; */ |
|---|
| 854 | |
|---|
| 855 | /* Update the realtime status */ |
|---|
| 856 | data->alarms = w83792d_read_value(client, W83792D_REG_ALARM1) + |
|---|
| 857 | (w83792d_read_value(client, W83792D_REG_ALARM2) << 8) + |
|---|
| 858 | (w83792d_read_value(client, W83792D_REG_ALARM3) << 16); |
|---|
| 859 | |
|---|
| 860 | /* Update CaseOpen status and it's CLR_CHS. */ |
|---|
| 861 | data->chassis[0] = (w83792d_read_value(client, |
|---|
| 862 | W83792D_REG_CASE_OPEN) |
|---|
| 863 | >> 5) & 0x01; |
|---|
| 864 | data->chassis[1] = (w83792d_read_value(client, |
|---|
| 865 | W83792D_REG_CASE_OPEN_CLR) |
|---|
| 866 | >> 7) & 0x01; |
|---|
| 867 | |
|---|
| 868 | /* Update Thermal Cruise/Smart Fan I target value */ |
|---|
| 869 | for (i = 0; i < 3; i++) { |
|---|
| 870 | data->thermal_cruise[i] = |
|---|
| 871 | w83792d_read_value(client, |
|---|
| 872 | W83792D_REG_THERMAL[i]) & 0x7f; |
|---|
| 873 | } |
|---|
| 874 | |
|---|
| 875 | /* Update Smart Fan I/II tolerance */ |
|---|
| 876 | reg_tmp = w83792d_read_value(client, W83792D_REG_FAN_TOL[0]); |
|---|
| 877 | data->fan_tolerance[0] = reg_tmp & 0x0f; |
|---|
| 878 | data->fan_tolerance[1] = (reg_tmp >> 4) & 0x0f; |
|---|
| 879 | data->fan_tolerance[2] = |
|---|
| 880 | w83792d_read_value(client, W83792D_REG_FAN_TOL[2]) & 0x0f; |
|---|
| 881 | |
|---|
| 882 | /* Update Smart Fan II temperature points */ |
|---|
| 883 | for (i = 0; i < 3; i++) { |
|---|
| 884 | for (j = 0; j < 4; j++) { |
|---|
| 885 | data->sf2_points[i][j] = w83792d_read_value( |
|---|
| 886 | client,W83792D_REG_POINTS[i][j]) & 0x7f; |
|---|
| 887 | } |
|---|
| 888 | } |
|---|
| 889 | |
|---|
| 890 | /* Update Smart Fan II duty cycle levels */ |
|---|
| 891 | for (i = 0; i < 3; i++) { |
|---|
| 892 | reg_tmp = w83792d_read_value(client, |
|---|
| 893 | W83792D_REG_LEVELS[i][0]); |
|---|
| 894 | data->sf2_levels[i][0] = reg_tmp & 0x0f; |
|---|
| 895 | data->sf2_levels[i][1] = (reg_tmp >> 4) & 0x0f; |
|---|
| 896 | reg_tmp = w83792d_read_value(client, |
|---|
| 897 | W83792D_REG_LEVELS[i][2]); |
|---|
| 898 | data->sf2_levels[i][2] = (reg_tmp >> 4) & 0x0f; |
|---|
| 899 | data->sf2_levels[i][3] = reg_tmp & 0x0f; |
|---|
| 900 | } |
|---|
| 901 | data->last_updated = jiffies; |
|---|
| 902 | data->valid = 1; |
|---|
| 903 | #ifdef W83792D_DEBUG |
|---|
| 904 | w83792d_print_debug(data); |
|---|
| 905 | #endif |
|---|
| 906 | } |
|---|
| 907 | up(&data->update_lock); |
|---|
| 908 | } |
|---|
| 909 | |
|---|
| 910 | /* This is a function used to debug the message. */ |
|---|
| 911 | #ifdef W83792D_DEBUG |
|---|
| 912 | static void w83792d_print_debug(struct w83792d_data *data) |
|---|
| 913 | { |
|---|
| 914 | int i=0, j=0; |
|---|
| 915 | printk(KERN_DEBUG "==========The following is the debug message...========\n"); |
|---|
| 916 | printk(KERN_DEBUG "9 set of Voltages: =====>\n"); |
|---|
| 917 | for (i=0; i<=8; i++) { |
|---|
| 918 | printk(KERN_DEBUG "vin[%d] is: 0x%x\n", i, data->in[i]); |
|---|
| 919 | printk(KERN_DEBUG "vin[%d] max is: 0x%x\n", i, data->in_max[i]); |
|---|
| 920 | printk(KERN_DEBUG "vin[%d] min is: 0x%x\n", i, data->in_min[i]); |
|---|
| 921 | } |
|---|
| 922 | printk(KERN_DEBUG "Low Bit1 is: 0x%x\n", data->low_bits & 0xff); |
|---|
| 923 | printk(KERN_DEBUG "Low Bit2 is: 0x%x\n", data->low_bits >> 8); |
|---|
| 924 | printk(KERN_DEBUG "7 set of Fan Counts and 3 set of Duty Cycles: =====>\n"); |
|---|
| 925 | printk(KERN_DEBUG "fan_cfg is: 0x%x\n", data->fan_cfg); |
|---|
| 926 | for (i=0; i<=6; i++) { |
|---|
| 927 | printk(KERN_DEBUG "fan[%d] is: 0x%x\n", i, data->fan[i]); |
|---|
| 928 | printk(KERN_DEBUG "fan[%d] min is: 0x%x\n", i, data->fan_min[i]); |
|---|
| 929 | if (i<3) { |
|---|
| 930 | printk(KERN_DEBUG "pwm[%d] is: 0x%x\n", i, data->pwm[i]); |
|---|
| 931 | printk(KERN_DEBUG "pwm_flag[%d] is: 0x%x\n", i, data->pwm_flag[i]); |
|---|
| 932 | } |
|---|
| 933 | } |
|---|
| 934 | printk(KERN_DEBUG "3 set of Temperatures: =====>\n"); |
|---|
| 935 | printk(KERN_DEBUG "temp1 is: 0x%x\n", data->temp1[0]); |
|---|
| 936 | printk(KERN_DEBUG "temp1 high limit is: 0x%x\n", data->temp1[1]); |
|---|
| 937 | printk(KERN_DEBUG "temp1 low limit is: 0x%x\n", data->temp1[2]); |
|---|
| 938 | for (i=0; i<2; i++) { |
|---|
| 939 | for (j=0; j<7; j++) { |
|---|
| 940 | printk(KERN_DEBUG "temp_add[%d][%d] is: 0x%x\n", i, j, |
|---|
| 941 | data->temp_add[i][j]); |
|---|
| 942 | } |
|---|
| 943 | } |
|---|
| 944 | for (i=0; i<=6; i++) { |
|---|
| 945 | printk(KERN_DEBUG "fan_div[%d] is: 0x%x\n", i, data->fan_div[i]); |
|---|
| 946 | } |
|---|
| 947 | printk(KERN_DEBUG "==========End of the debug message...==================\n\n"); |
|---|
| 948 | } |
|---|
| 949 | #endif |
|---|
| 950 | |
|---|
| 951 | /* The next few functions are the call-back functions of the /proc/sys and |
|---|
| 952 | sysctl files. Which function is used is defined in the ctl_table in |
|---|
| 953 | the extra1 field. |
|---|
| 954 | Each function must return the magnitude (power of 10 to divide the date |
|---|
| 955 | with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must |
|---|
| 956 | put a maximum of *nrels elements in results reflecting the data of this |
|---|
| 957 | file, and set *nrels to the number it actually put in it, if operation== |
|---|
| 958 | SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from |
|---|
| 959 | results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. |
|---|
| 960 | Note that on SENSORS_PROC_REAL_READ, I do not check whether results is |
|---|
| 961 | large enough (by checking the incoming value of *nrels). This is not very |
|---|
| 962 | good practice, but as long as you put less than about 5 values in results, |
|---|
| 963 | you can assume it is large enough. */ |
|---|
| 964 | |
|---|
| 965 | /* read/write voltage meaured value and limits */ |
|---|
| 966 | static void w83792d_in(struct i2c_client *client, int operation, int ctl_name, |
|---|
| 967 | int *nrels_mag, long *results) |
|---|
| 968 | { |
|---|
| 969 | struct w83792d_data *data = client->data; |
|---|
| 970 | int nr = ctl_name - W83792D_SYSCTL_IN0; |
|---|
| 971 | |
|---|
| 972 | /* result[0]: low limit, result[1]: high limit, |
|---|
| 973 | result[2]: measured value */ |
|---|
| 974 | if (operation == SENSORS_PROC_REAL_INFO) |
|---|
| 975 | *nrels_mag = 3; |
|---|
| 976 | else if (operation == SENSORS_PROC_REAL_READ) { |
|---|
| 977 | w83792d_update_client(client); |
|---|
| 978 | results[0] = IN_FROM_REG(nr, data->in_min[nr]*4); |
|---|
| 979 | results[1] = IN_FROM_REG(nr, data->in_max[nr]*4); |
|---|
| 980 | /* in7 and in8 do not have low bits, but the formula still |
|---|
| 981 | works */ |
|---|
| 982 | results[2] = IN_FROM_REG(nr, ((data->in[nr] << 2) | |
|---|
| 983 | ((data->low_bits >> (2 * nr)) |
|---|
| 984 | & 0x03))); |
|---|
| 985 | *nrels_mag = 3; |
|---|
| 986 | } else if (operation == SENSORS_PROC_REAL_WRITE) { |
|---|
| 987 | if (*nrels_mag >= 1) { |
|---|
| 988 | /* Write Low limit into register. */ |
|---|
| 989 | data->in_min[nr] = SENSORS_LIMIT(IN_TO_REG(nr,results[0])/4, |
|---|
| 990 | 0, 255); |
|---|
| 991 | w83792d_write_value(client, W83792D_REG_IN_MIN[nr], |
|---|
| 992 | data->in_min[nr]); |
|---|
| 993 | } |
|---|
| 994 | if (*nrels_mag >= 2) { |
|---|
| 995 | /* Write High limit into register. */ |
|---|
| 996 | data->in_max[nr] = SENSORS_LIMIT(IN_TO_REG(nr,results[1])/4, |
|---|
| 997 | 0, 255); |
|---|
| 998 | w83792d_write_value(client, W83792D_REG_IN_MAX[nr], |
|---|
| 999 | data->in_max[nr]); |
|---|
| 1000 | } |
|---|
| 1001 | } |
|---|
| 1002 | } |
|---|
| 1003 | |
|---|
| 1004 | /* read/write fan meaured value and limits */ |
|---|
| 1005 | static void w83792d_fan(struct i2c_client *client, int operation, int ctl_name, |
|---|
| 1006 | int *nrels_mag, long *results) |
|---|
| 1007 | { |
|---|
| 1008 | struct w83792d_data *data = client->data; |
|---|
| 1009 | int nr = ctl_name - W83792D_SYSCTL_FAN1; |
|---|
| 1010 | u8 tmp_reg, tmp_fan_div; |
|---|
| 1011 | |
|---|
| 1012 | /* result[0]: low limit, result[1]: measured value */ |
|---|
| 1013 | if (operation == SENSORS_PROC_REAL_INFO) |
|---|
| 1014 | *nrels_mag = 0; |
|---|
| 1015 | else if (operation == SENSORS_PROC_REAL_READ) { |
|---|
| 1016 | w83792d_update_client(client); |
|---|
| 1017 | results[0] = FAN_FROM_REG(data->fan_min[nr], |
|---|
| 1018 | DIV_FROM_REG(data->fan_div[nr])); |
|---|
| 1019 | /* adjust Fan Divisor, then change RPM */ |
|---|
| 1020 | do { |
|---|
| 1021 | w83792d_update_client(client); |
|---|
| 1022 | if ((data->fan[nr]>0x50) && (data->fan[nr]<0xff)) { |
|---|
| 1023 | /* optimal case. 0x50 and 0xff are experience data */ |
|---|
| 1024 | results[1] = FAN_FROM_REG(data->fan[nr], |
|---|
| 1025 | DIV_FROM_REG(data->fan_div[nr])); |
|---|
| 1026 | break; /* go out of the do-while loop. */ |
|---|
| 1027 | } else { |
|---|
| 1028 | if (((data->fan_div[nr])>=0x07 && |
|---|
| 1029 | (data->fan[nr])==0xff) || |
|---|
| 1030 | ((data->fan_div[nr])<=0 && |
|---|
| 1031 | (data->fan[nr])<0x78)) { |
|---|
| 1032 | results[1] = 0; |
|---|
| 1033 | break; |
|---|
| 1034 | } else if ((data->fan_div[nr])<0x07 && |
|---|
| 1035 | (data->fan[nr])==0xff) { |
|---|
| 1036 | (data->fan_div[nr])++; |
|---|
| 1037 | results[1] = FAN_FROM_REG(data->fan[nr], |
|---|
| 1038 | DIV_FROM_REG(data->fan_div[nr])); |
|---|
| 1039 | } else if ((data->fan_div[nr])>0 && |
|---|
| 1040 | (data->fan[nr])<0x78) { |
|---|
| 1041 | (data->fan_div[nr])--; |
|---|
| 1042 | results[1] = FAN_FROM_REG(data->fan[nr], |
|---|
| 1043 | DIV_FROM_REG(data->fan_div[nr])); |
|---|
| 1044 | } |
|---|
| 1045 | |
|---|
| 1046 | tmp_reg = w83792d_read_value(client, |
|---|
| 1047 | W83792D_REG_FAN_DIV[nr/2]); |
|---|
| 1048 | tmp_reg &= (nr%2 == 0) ? 0xf8 : 0x8f; |
|---|
| 1049 | tmp_fan_div = (nr%2 == 0) ? (data->fan_div[nr]) |
|---|
| 1050 | : (((data->fan_div[nr])<<4)&0x70); |
|---|
| 1051 | w83792d_write_value(client, |
|---|
| 1052 | W83792D_REG_FAN_DIV[nr/2], |
|---|
| 1053 | tmp_reg|tmp_fan_div); |
|---|
| 1054 | } |
|---|
| 1055 | } while (0); |
|---|
| 1056 | *nrels_mag = 2; |
|---|
| 1057 | } else if (operation == SENSORS_PROC_REAL_WRITE) { |
|---|
| 1058 | if (*nrels_mag >= 1 && (data->has_fan & (1 << nr))) { |
|---|
| 1059 | data->fan_min[nr] = FAN_TO_REG(results[0], |
|---|
| 1060 | DIV_FROM_REG(data->fan_div[nr])); |
|---|
| 1061 | w83792d_write_value(client, |
|---|
| 1062 | W83792D_REG_FAN_MIN[nr], |
|---|
| 1063 | data->fan_min[nr]); |
|---|
| 1064 | } |
|---|
| 1065 | } |
|---|
| 1066 | } |
|---|
| 1067 | |
|---|
| 1068 | /* read/write temperature1 meaured value and limits */ |
|---|
| 1069 | static void w83792d_temp(struct i2c_client *client, int operation, int ctl_name, |
|---|
| 1070 | int *nrels_mag, long *results) |
|---|
| 1071 | { |
|---|
| 1072 | struct w83792d_data *data = client->data; |
|---|
| 1073 | |
|---|
| 1074 | /* result[0]: high limit, result[1]: low limit |
|---|
| 1075 | result[2]: measured value, the order is different with voltage(in) */ |
|---|
| 1076 | if (operation == SENSORS_PROC_REAL_INFO) { |
|---|
| 1077 | *nrels_mag = 1; |
|---|
| 1078 | } else if (operation == SENSORS_PROC_REAL_READ) { |
|---|
| 1079 | w83792d_update_client(client); |
|---|
| 1080 | results[0] = TEMP_FROM_REG(data->temp1[1]); |
|---|
| 1081 | results[1] = TEMP_FROM_REG(data->temp1[2]); |
|---|
| 1082 | results[2] = TEMP_FROM_REG(data->temp1[0]); |
|---|
| 1083 | *nrels_mag = 3; |
|---|
| 1084 | } else if (operation == SENSORS_PROC_REAL_WRITE) { |
|---|
| 1085 | if (*nrels_mag >= 1) { |
|---|
| 1086 | data->temp1[1] = TEMP_TO_REG(results[0]); |
|---|
| 1087 | w83792d_write_value(client, W83792D_REG_TEMP1_OVER, |
|---|
| 1088 | data->temp1[1]); |
|---|
| 1089 | } |
|---|
| 1090 | if (*nrels_mag >= 2) { |
|---|
| 1091 | data->temp1[2] = TEMP_TO_REG(results[1]); |
|---|
| 1092 | w83792d_write_value(client, W83792D_REG_TEMP1_HYST, |
|---|
| 1093 | data->temp1[2]); |
|---|
| 1094 | } |
|---|
| 1095 | } |
|---|
| 1096 | } |
|---|
| 1097 | |
|---|
| 1098 | /* read/write temperature2,3 meaured value and limits */ |
|---|
| 1099 | static void w83792d_temp_add(struct i2c_client *client, int operation, |
|---|
| 1100 | int ctl_name, int *nrels_mag, long *results) |
|---|
| 1101 | { |
|---|
| 1102 | struct w83792d_data *data = client->data; |
|---|
| 1103 | int nr = ctl_name - W83792D_SYSCTL_TEMP2; |
|---|
| 1104 | int i=0, j=0; |
|---|
| 1105 | |
|---|
| 1106 | /* result[0]: high limit, result[1]: low limit |
|---|
| 1107 | result[2]: measured value, the order is different with voltage(in) */ |
|---|
| 1108 | if (operation == SENSORS_PROC_REAL_INFO) |
|---|
| 1109 | *nrels_mag = 1; |
|---|
| 1110 | else if (operation == SENSORS_PROC_REAL_READ) { |
|---|
| 1111 | w83792d_update_client(client); |
|---|
| 1112 | for (i=0; i<3; i++) { |
|---|
| 1113 | j = (i==0) ? 2 : ((i==1)?0:1); |
|---|
| 1114 | if ((data->temp_add[nr][i*2+1]) & 0x80) { |
|---|
| 1115 | results[j] = TEMP_FROM_REG(data->temp_add[nr][i*2]) + 5; |
|---|
| 1116 | } else { |
|---|
| 1117 | results[j] = TEMP_FROM_REG(data->temp_add[nr][i*2]); |
|---|
| 1118 | } |
|---|
| 1119 | } |
|---|
| 1120 | *nrels_mag = 3; |
|---|
| 1121 | } else if (operation == SENSORS_PROC_REAL_WRITE) { |
|---|
| 1122 | if (*nrels_mag >= 1) { |
|---|
| 1123 | data->temp_add[nr][2] = TEMP_TO_REG(results[0]); |
|---|
| 1124 | w83792d_write_value(client, |
|---|
| 1125 | W83792D_REG_TEMP_ADD[nr][2], |
|---|
| 1126 | data->temp_add[nr][2]); |
|---|
| 1127 | if ((results[0]%10) == 0) { |
|---|
| 1128 | w83792d_write_value(client, |
|---|
| 1129 | W83792D_REG_TEMP_ADD[nr][3], 0x00); |
|---|
| 1130 | } else { /* consider the 0.5 degree */ |
|---|
| 1131 | w83792d_write_value(client, |
|---|
| 1132 | W83792D_REG_TEMP_ADD[nr][3], 0x80); |
|---|
| 1133 | } |
|---|
| 1134 | } |
|---|
| 1135 | if (*nrels_mag >= 2) { |
|---|
| 1136 | data->temp_add[nr][4] = TEMP_TO_REG(results[1]); |
|---|
| 1137 | w83792d_write_value(client, |
|---|
| 1138 | W83792D_REG_TEMP_ADD[nr][4], |
|---|
| 1139 | data->temp_add[nr][4]); |
|---|
| 1140 | if ((results[1]%10) == 0) { |
|---|
| 1141 | w83792d_write_value(client, |
|---|
| 1142 | W83792D_REG_TEMP_ADD[nr][5], 0x00); |
|---|
| 1143 | } else { /* consider the 0.5 degree */ |
|---|
| 1144 | w83792d_write_value(client, |
|---|
| 1145 | W83792D_REG_TEMP_ADD[nr][5], 0x80); |
|---|
| 1146 | } |
|---|
| 1147 | } |
|---|
| 1148 | } |
|---|
| 1149 | } |
|---|
| 1150 | |
|---|
| 1151 | /* |
|---|
| 1152 | void w83792d_vid(struct i2c_client *client, int operation, int ctl_name, |
|---|
| 1153 | int *nrels_mag, long *results) |
|---|
| 1154 | { |
|---|
| 1155 | struct w83792d_data *data = client->data; |
|---|
| 1156 | if (operation == SENSORS_PROC_REAL_INFO) |
|---|
| 1157 | *nrels_mag = 3; |
|---|
| 1158 | else if (operation == SENSORS_PROC_REAL_READ) { |
|---|
| 1159 | w83792d_update_client(client); |
|---|
| 1160 | results[0] = vid_from_reg(data->vid, data->vrm); |
|---|
| 1161 | *nrels_mag = 1; |
|---|
| 1162 | } |
|---|
| 1163 | } |
|---|
| 1164 | |
|---|
| 1165 | void w83792d_vrm(struct i2c_client *client, int operation, int ctl_name, |
|---|
| 1166 | int *nrels_mag, long *results) |
|---|
| 1167 | { |
|---|
| 1168 | struct w83792d_data *data = client->data; |
|---|
| 1169 | if (operation == SENSORS_PROC_REAL_INFO) |
|---|
| 1170 | *nrels_mag = 1; |
|---|
| 1171 | else if (operation == SENSORS_PROC_REAL_READ) { |
|---|
| 1172 | results[0] = data->vrm; |
|---|
| 1173 | *nrels_mag = 1; |
|---|
| 1174 | } else if (operation == SENSORS_PROC_REAL_WRITE) { |
|---|
| 1175 | if (*nrels_mag >= 1) { |
|---|
| 1176 | data->vrm = results[0]; |
|---|
| 1177 | } |
|---|
| 1178 | } |
|---|
| 1179 | } */ |
|---|
| 1180 | |
|---|
| 1181 | /* Read/Write Fan Divisor */ |
|---|
| 1182 | static void w83792d_fan_div(struct i2c_client *client, int operation, |
|---|
| 1183 | int ctl_name, int *nrels_mag, long *results) |
|---|
| 1184 | { |
|---|
| 1185 | struct w83792d_data *data = client->data; |
|---|
| 1186 | int i=0, j=0; |
|---|
| 1187 | u8 temp_reg=0, k=1, fan_div_reg=0; |
|---|
| 1188 | u8 tmp_fan_div; |
|---|
| 1189 | |
|---|
| 1190 | if (operation == SENSORS_PROC_REAL_INFO) |
|---|
| 1191 | *nrels_mag = 0; |
|---|
| 1192 | else if (operation == SENSORS_PROC_REAL_READ) { |
|---|
| 1193 | w83792d_update_client(client); |
|---|
| 1194 | for (i=0; i<7; i++) { |
|---|
| 1195 | results[i] = DIV_FROM_REG(data->fan_div[i]); |
|---|
| 1196 | } |
|---|
| 1197 | *nrels_mag = 7; |
|---|
| 1198 | } else if (operation == SENSORS_PROC_REAL_WRITE) { |
|---|
| 1199 | if (*nrels_mag < 7) { |
|---|
| 1200 | return; |
|---|
| 1201 | } |
|---|
| 1202 | for (i=0; i<7; i++) { |
|---|
| 1203 | temp_reg = SENSORS_LIMIT(results[i], 1, 128); |
|---|
| 1204 | for (k=0,j=0; j<7; j++) { |
|---|
| 1205 | temp_reg = temp_reg>>1; |
|---|
| 1206 | if (temp_reg == 0) |
|---|
| 1207 | break; |
|---|
| 1208 | k++; |
|---|
| 1209 | } |
|---|
| 1210 | fan_div_reg = w83792d_read_value(client, |
|---|
| 1211 | W83792D_REG_FAN_DIV[i/2]); |
|---|
| 1212 | fan_div_reg &= (i%2 == 0) ? 0xf8 : 0x8f; |
|---|
| 1213 | tmp_fan_div = (i%2 == 0) ? (k&0x07) |
|---|
| 1214 | : ((k<<4)&0x70); |
|---|
| 1215 | w83792d_write_value(client, |
|---|
| 1216 | W83792D_REG_FAN_DIV[i/2], |
|---|
| 1217 | fan_div_reg|tmp_fan_div); |
|---|
| 1218 | } |
|---|
| 1219 | } |
|---|
| 1220 | } |
|---|
| 1221 | |
|---|
| 1222 | |
|---|
| 1223 | /* Under Smart Fan I mode: read/write the Fan1/2/3 target temperature */ |
|---|
| 1224 | static void w83792d_thermal_cruise(struct i2c_client *client, int operation, |
|---|
| 1225 | int ctl_name, int *nrels_mag, long *results) |
|---|
| 1226 | { |
|---|
| 1227 | struct w83792d_data *data = client->data; |
|---|
| 1228 | int i=0; |
|---|
| 1229 | u8 target_tmp=0, target_mask=0; |
|---|
| 1230 | |
|---|
| 1231 | if (operation == SENSORS_PROC_REAL_INFO) |
|---|
| 1232 | *nrels_mag = 0; |
|---|
| 1233 | else if (operation == SENSORS_PROC_REAL_READ) { |
|---|
| 1234 | w83792d_update_client(client); |
|---|
| 1235 | for (i=0; i<3; i++) { |
|---|
| 1236 | results[i] = data->thermal_cruise[i]; |
|---|
| 1237 | } |
|---|
| 1238 | *nrels_mag = 3; |
|---|
| 1239 | } else if (operation == SENSORS_PROC_REAL_WRITE) { |
|---|
| 1240 | for (i=0; i<3; i++) { |
|---|
| 1241 | if (*nrels_mag < (i+1)) { |
|---|
| 1242 | return; |
|---|
| 1243 | } |
|---|
| 1244 | target_tmp = results[i]; |
|---|
| 1245 | target_tmp = target_tmp & 0x7f; |
|---|
| 1246 | target_mask = w83792d_read_value(client, |
|---|
| 1247 | W83792D_REG_THERMAL[i]) & 0x80; |
|---|
| 1248 | data->thermal_cruise[i] = SENSORS_LIMIT(target_tmp, 0, 255); |
|---|
| 1249 | w83792d_write_value(client, W83792D_REG_THERMAL[i], |
|---|
| 1250 | (data->thermal_cruise[i])|target_mask); |
|---|
| 1251 | } |
|---|
| 1252 | } |
|---|
| 1253 | } |
|---|
| 1254 | |
|---|
| 1255 | /* The tolerance of fan1/fan2/fan3, when using Thermal Cruise(Smart Fan I) |
|---|
| 1256 | or Smart Fan II mode. */ |
|---|
| 1257 | static void w83792d_fan_tolerance(struct i2c_client *client, int operation, |
|---|
| 1258 | int ctl_name, int *nrels_mag, long *results) |
|---|
| 1259 | { |
|---|
| 1260 | struct w83792d_data *data = client->data; |
|---|
| 1261 | int i=0; |
|---|
| 1262 | u8 tol_tmp, tol_mask; |
|---|
| 1263 | |
|---|
| 1264 | if (operation == SENSORS_PROC_REAL_INFO) |
|---|
| 1265 | *nrels_mag = 0; |
|---|
| 1266 | else if (operation == SENSORS_PROC_REAL_READ) { |
|---|
| 1267 | w83792d_update_client(client); |
|---|
| 1268 | for (i=0; i<3; i++) { |
|---|
| 1269 | results[i] = data->fan_tolerance[i]; |
|---|
| 1270 | } |
|---|
| 1271 | *nrels_mag = 3; |
|---|
| 1272 | } else if (operation == SENSORS_PROC_REAL_WRITE) { |
|---|
| 1273 | for (i=0; i<3; i++) { |
|---|
| 1274 | if (*nrels_mag < (i+1)) { |
|---|
| 1275 | return; |
|---|
| 1276 | } |
|---|
| 1277 | tol_mask = w83792d_read_value(client, |
|---|
| 1278 | W83792D_REG_FAN_TOL[i]) & ((i==1)?0x0f:0xf0); |
|---|
| 1279 | tol_tmp = SENSORS_LIMIT(results[i], 0, 15); |
|---|
| 1280 | tol_tmp &= 0x0f; |
|---|
| 1281 | data->fan_tolerance[i] = tol_tmp; |
|---|
| 1282 | if (i==1) { |
|---|
| 1283 | tol_tmp <<= 4; |
|---|
| 1284 | } |
|---|
| 1285 | w83792d_write_value(client, W83792D_REG_FAN_TOL[i], |
|---|
| 1286 | tol_mask|tol_tmp); |
|---|
| 1287 | } |
|---|
| 1288 | } |
|---|
| 1289 | } |
|---|
| 1290 | |
|---|
| 1291 | /* Under Smart Fan II mode: read/write the Fan1/2/3 temperature points */ |
|---|
| 1292 | static void w83792d_sf2_points(struct i2c_client *client, int operation, |
|---|
| 1293 | int ctl_name, int *nrels_mag, long *results) |
|---|
| 1294 | { |
|---|
| 1295 | struct w83792d_data *data = client->data; |
|---|
| 1296 | int nr = ctl_name - W83792D_SYSCTL_SF2_POINTS_FAN1; |
|---|
| 1297 | int j=0; |
|---|
| 1298 | u8 mask_tmp = 0; |
|---|
| 1299 | |
|---|
| 1300 | if (operation == SENSORS_PROC_REAL_INFO) |
|---|
| 1301 | *nrels_mag = 0; |
|---|
| 1302 | else if (operation == SENSORS_PROC_REAL_READ) { |
|---|
| 1303 | w83792d_update_client(client); |
|---|
| 1304 | for (j=0; j<4; j++) { |
|---|
| 1305 | results[j] = data->sf2_points[nr][j]; |
|---|
| 1306 | } |
|---|
| 1307 | *nrels_mag = 4; |
|---|
| 1308 | } else if (operation == SENSORS_PROC_REAL_WRITE) { |
|---|
| 1309 | for (j=0; j<4; j++) { |
|---|
| 1310 | if (*nrels_mag < (j+1)) { |
|---|
| 1311 | return; |
|---|
| 1312 | } |
|---|
| 1313 | data->sf2_points[nr][j] = SENSORS_LIMIT(results[j], |
|---|
| 1314 | 0, 127); |
|---|
| 1315 | mask_tmp = w83792d_read_value(client, |
|---|
| 1316 | W83792D_REG_POINTS[nr][j]) & 0x80; |
|---|
| 1317 | w83792d_write_value(client, W83792D_REG_POINTS[nr][j], |
|---|
| 1318 | mask_tmp|data->sf2_points[nr][j]); |
|---|
| 1319 | } |
|---|
| 1320 | } |
|---|
| 1321 | } |
|---|
| 1322 | |
|---|
| 1323 | /* Smart Fan II Duty Cycle1/2/3 of Fan1/2/3. |
|---|
| 1324 | Notice that: The Non-Stop can NOT be modified by user, |
|---|
| 1325 | because it is related with some physical characters, |
|---|
| 1326 | usually set by BIOS. User's modification to it may lead to |
|---|
| 1327 | Fan's stop, then bring danger. */ |
|---|
| 1328 | static void w83792d_sf2_levels(struct i2c_client *client, int operation, |
|---|
| 1329 | int ctl_name, int *nrels_mag, long *results) |
|---|
| 1330 | { |
|---|
| 1331 | struct w83792d_data *data = client->data; |
|---|
| 1332 | int nr = ctl_name - W83792D_SYSCTL_SF2_LEVELS_FAN1; |
|---|
| 1333 | int j = 0; |
|---|
| 1334 | u8 mask_tmp = 0, level_tmp = 0; |
|---|
| 1335 | |
|---|
| 1336 | if (operation == SENSORS_PROC_REAL_INFO) |
|---|
| 1337 | *nrels_mag = 0; |
|---|
| 1338 | else if (operation == SENSORS_PROC_REAL_READ) { |
|---|
| 1339 | w83792d_update_client(client); |
|---|
| 1340 | for (j=0; j<4; j++) { |
|---|
| 1341 | results[j] = (data->sf2_levels[nr][j] * 100) / 15; |
|---|
| 1342 | } |
|---|
| 1343 | *nrels_mag = 4; |
|---|
| 1344 | } else if (operation == SENSORS_PROC_REAL_WRITE) { |
|---|
| 1345 | for (j=1; j<4; j++) { /* start with 1: need ignore Non-Stop */ |
|---|
| 1346 | if (*nrels_mag < j) { |
|---|
| 1347 | return; |
|---|
| 1348 | } |
|---|
| 1349 | data->sf2_levels[nr][j] = |
|---|
| 1350 | SENSORS_LIMIT((results[j]*15)/100, 0, 15); |
|---|
| 1351 | mask_tmp = w83792d_read_value(client, W83792D_REG_LEVELS[nr][j]) |
|---|
| 1352 | & ((j==3) ? 0xf0 : 0x0f); |
|---|
| 1353 | if (j==3) { |
|---|
| 1354 | level_tmp = data->sf2_levels[nr][j]; |
|---|
| 1355 | } else { |
|---|
| 1356 | level_tmp = data->sf2_levels[nr][j] << 4; |
|---|
| 1357 | } |
|---|
| 1358 | w83792d_write_value(client, W83792D_REG_LEVELS[nr][j], |
|---|
| 1359 | level_tmp | mask_tmp); |
|---|
| 1360 | } |
|---|
| 1361 | } |
|---|
| 1362 | } |
|---|
| 1363 | |
|---|
| 1364 | /* get reatime status of all sensors items: voltage, temp, fan */ |
|---|
| 1365 | static void w83792d_alarms(struct i2c_client *client, int operation, int ctl_name, |
|---|
| 1366 | int *nrels_mag, long *results) |
|---|
| 1367 | { |
|---|
| 1368 | struct w83792d_data *data = client->data; |
|---|
| 1369 | if (operation == SENSORS_PROC_REAL_INFO) |
|---|
| 1370 | *nrels_mag = 0; |
|---|
| 1371 | else if (operation == SENSORS_PROC_REAL_READ) { |
|---|
| 1372 | w83792d_update_client(client); |
|---|
| 1373 | results[0] = data->alarms; |
|---|
| 1374 | *nrels_mag = 1; |
|---|
| 1375 | } |
|---|
| 1376 | } |
|---|
| 1377 | |
|---|
| 1378 | /* Read/Write Chassis status and Reset Chassis. */ |
|---|
| 1379 | static void w83792d_chassis(struct i2c_client *client, int operation, |
|---|
| 1380 | int ctl_name, int *nrels_mag, long *results) |
|---|
| 1381 | { |
|---|
| 1382 | struct w83792d_data *data = client->data; |
|---|
| 1383 | u8 temp1 = 0, temp2 = 0; |
|---|
| 1384 | |
|---|
| 1385 | if (operation == SENSORS_PROC_REAL_INFO) |
|---|
| 1386 | *nrels_mag = 0; |
|---|
| 1387 | else if (operation == SENSORS_PROC_REAL_READ) { |
|---|
| 1388 | w83792d_update_client(client); |
|---|
| 1389 | results[0] = data->chassis[0]; |
|---|
| 1390 | results[1] = data->chassis[1]; |
|---|
| 1391 | *nrels_mag = 2; |
|---|
| 1392 | } else if (operation == SENSORS_PROC_REAL_WRITE) { |
|---|
| 1393 | data->chassis[1] = SENSORS_LIMIT(results[1], 0 ,1); |
|---|
| 1394 | temp1 = ((data->chassis[1]) << 7) & 0x80; |
|---|
| 1395 | temp2 = w83792d_read_value(client, |
|---|
| 1396 | W83792D_REG_CASE_OPEN_CLR) & 0x7f; |
|---|
| 1397 | w83792d_write_value(client, |
|---|
| 1398 | W83792D_REG_CASE_OPEN_CLR, |
|---|
| 1399 | temp1|temp2); |
|---|
| 1400 | } |
|---|
| 1401 | } |
|---|
| 1402 | |
|---|
| 1403 | /* Read/Write PWM/DC value of Fan1,Fan2,Fan3, which controls the |
|---|
| 1404 | Fan Duty Cycle */ |
|---|
| 1405 | static void w83792d_pwm(struct i2c_client *client, int operation, int ctl_name, |
|---|
| 1406 | int *nrels_mag, long *results) |
|---|
| 1407 | { |
|---|
| 1408 | struct w83792d_data *data = client->data; |
|---|
| 1409 | int nr = ctl_name - W83792D_SYSCTL_PWM1; |
|---|
| 1410 | u8 pwm_mask; |
|---|
| 1411 | |
|---|
| 1412 | if (operation == SENSORS_PROC_REAL_INFO) |
|---|
| 1413 | *nrels_mag = 0; |
|---|
| 1414 | else if (operation == SENSORS_PROC_REAL_READ) { |
|---|
| 1415 | w83792d_update_client(client); |
|---|
| 1416 | results[0] = data->pwm[nr]; |
|---|
| 1417 | *nrels_mag = 1; |
|---|
| 1418 | } else if (operation == SENSORS_PROC_REAL_WRITE) { |
|---|
| 1419 | data->pwm[nr] = SENSORS_LIMIT(results[0], 0, 15); |
|---|
| 1420 | pwm_mask = w83792d_read_value(client,W83792D_REG_PWM[nr]) & 0xf0; |
|---|
| 1421 | w83792d_write_value(client,W83792D_REG_PWM[nr],pwm_mask|data->pwm[nr]); |
|---|
| 1422 | } |
|---|
| 1423 | } |
|---|
| 1424 | |
|---|
| 1425 | /* Read/Write PWM/DC mode for Fan1,Fan2,Fan3: |
|---|
| 1426 | 1->PWM mode, 0->DC mode */ |
|---|
| 1427 | static void w83792d_pwm_flag(struct i2c_client *client, int operation, int ctl_name, |
|---|
| 1428 | int *nrels_mag, long *results) |
|---|
| 1429 | { |
|---|
| 1430 | struct w83792d_data *data = client->data; |
|---|
| 1431 | int i = 0; |
|---|
| 1432 | u8 pwm_flag_mask; |
|---|
| 1433 | |
|---|
| 1434 | if (operation == SENSORS_PROC_REAL_INFO) |
|---|
| 1435 | *nrels_mag = 0; |
|---|
| 1436 | else if (operation == SENSORS_PROC_REAL_READ) { |
|---|
| 1437 | w83792d_update_client(client); |
|---|
| 1438 | for (i=0; i<3; i++) { |
|---|
| 1439 | results[i] = data->pwm_flag[i]; |
|---|
| 1440 | } |
|---|
| 1441 | *nrels_mag = 3; |
|---|
| 1442 | } else if (operation == SENSORS_PROC_REAL_WRITE) { |
|---|
| 1443 | for (i=0; i<3; i++) { |
|---|
| 1444 | if (*nrels_mag < (i+1)) { |
|---|
| 1445 | return; |
|---|
| 1446 | } |
|---|
| 1447 | data->pwm_flag[i] = SENSORS_LIMIT(results[i], 0, 1); |
|---|
| 1448 | pwm_flag_mask = w83792d_read_value(client, |
|---|
| 1449 | W83792D_REG_PWM[i]) & 0x7f; |
|---|
| 1450 | w83792d_write_value(client, W83792D_REG_PWM[i], |
|---|
| 1451 | ((data->pwm_flag[i])<<7)|pwm_flag_mask); |
|---|
| 1452 | } |
|---|
| 1453 | } |
|---|
| 1454 | } |
|---|
| 1455 | |
|---|
| 1456 | /* Read/Write Fan mode into:PWM/DC, Thermal Cruise(SmartFanI), SmartFanII |
|---|
| 1457 | 0->PWM/DC mode, 1->Thermal Cruise mode, 2/3->SmartFanII mode */ |
|---|
| 1458 | static void w83792d_fan_cfg(struct i2c_client *client, int operation, |
|---|
| 1459 | int ctl_name, int *nrels_mag, long *results) |
|---|
| 1460 | { |
|---|
| 1461 | struct w83792d_data *data = client->data; |
|---|
| 1462 | u8 temp_cfg1, temp_cfg2, temp_cfg3, temp_cfg4; |
|---|
| 1463 | |
|---|
| 1464 | if (operation == SENSORS_PROC_REAL_INFO) |
|---|
| 1465 | *nrels_mag = 0; |
|---|
| 1466 | else if (operation == SENSORS_PROC_REAL_READ) { |
|---|
| 1467 | w83792d_update_client(client); |
|---|
| 1468 | results[0] = (data->fan_cfg) & 0x03; /* Fan1's Mode */ |
|---|
| 1469 | results[1] = ((data->fan_cfg)>>2) & 0x03; /* Fan2's Mode */ |
|---|
| 1470 | results[2] = ((data->fan_cfg)>>4) & 0x03; /* Fan3's Mode */ |
|---|
| 1471 | *nrels_mag = 3; |
|---|
| 1472 | } else if (operation == SENSORS_PROC_REAL_WRITE) { |
|---|
| 1473 | if (*nrels_mag < 3) { |
|---|
| 1474 | return; |
|---|
| 1475 | } |
|---|
| 1476 | temp_cfg1 = SENSORS_LIMIT(results[0], 0, 3); |
|---|
| 1477 | temp_cfg2 = SENSORS_LIMIT(results[1], 0, 3) << 2; |
|---|
| 1478 | temp_cfg3 = SENSORS_LIMIT(results[2], 0, 3) << 4; |
|---|
| 1479 | temp_cfg4 = w83792d_read_value(client,W83792D_REG_FAN_CFG) & 0xc0; |
|---|
| 1480 | data->fan_cfg = ((temp_cfg4|temp_cfg3)|temp_cfg2)|temp_cfg1; |
|---|
| 1481 | w83792d_write_value(client,W83792D_REG_FAN_CFG,data->fan_cfg); |
|---|
| 1482 | } |
|---|
| 1483 | } |
|---|
| 1484 | |
|---|
| 1485 | static int __init sm_w83792d_init(void) |
|---|
| 1486 | { |
|---|
| 1487 | ENTER() |
|---|
| 1488 | |
|---|
| 1489 | printk(KERN_INFO "w83792d version %s (%s)\n", LM_VERSION, LM_DATE); |
|---|
| 1490 | |
|---|
| 1491 | LEAVE() |
|---|
| 1492 | return i2c_add_driver(&w83792d_driver); |
|---|
| 1493 | } |
|---|
| 1494 | |
|---|
| 1495 | static void __exit sm_w83792d_exit(void) |
|---|
| 1496 | { |
|---|
| 1497 | ENTER() |
|---|
| 1498 | |
|---|
| 1499 | i2c_del_driver(&w83792d_driver); |
|---|
| 1500 | |
|---|
| 1501 | LEAVE() |
|---|
| 1502 | } |
|---|
| 1503 | |
|---|
| 1504 | |
|---|
| 1505 | MODULE_AUTHOR("Chunhao Huang @ Winbond"); |
|---|
| 1506 | MODULE_DESCRIPTION("W83792AD/D driver for linux-2.4"); |
|---|
| 1507 | MODULE_LICENSE("GPL"); |
|---|
| 1508 | |
|---|
| 1509 | module_init(sm_w83792d_init); |
|---|
| 1510 | module_exit(sm_w83792d_exit); |
|---|
| 1511 | |
|---|