Newer
Older
/*
w83627ehf - Driver for the hardware monitoring functionality of
the Winbond W83627EHF Super-I/O chip
Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
Rudolf Marek <r.marek@assembler.cz>
David Hubbard <david.c.hubbard@gmail.com>
Copyright (C) 2010 Sheng-Yuan Huang (Nuvoton) (PS00)
Shamelessly ripped from the w83627hf driver
Copyright (C) 2003 Mark Studebaker
Thanks to Leon Moonen, Steve Cliffe and Grant Coady for their help
in testing and debugging this driver.
This driver also supports the W83627EHG, which is the lead-free
version of the W83627EHF.
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.
Supports the following chips:
Chip #vin #fan #pwm #temp chip IDs man ID
w83627ehf 10 5 4 3 0x8850 0x88 0x5ca3
w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3
w83627dhg-p 9 5 4 3 0xb070 0xc1 0x5ca3
w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3
w83667hg-b 9 5 3 4 0xb350 0xc1 0x5ca3
nct6775f 9 4 3 9 0xb470 0xc1 0x5ca3
nct6776f 9 5 3 9 0xC330 0xc1 0x5ca3
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/platform_device.h>
#include <linux/hwmon.h>
#include <linux/hwmon-vid.h>
#include <linux/err.h>
#include <linux/io.h>
#include "lm75.h"
enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg, w83667hg_b, nct6775,
nct6776 };
/* used to set data->name = w83627ehf_device_names[data->sio_kind] */
static const char * const w83627ehf_device_names[] = {
"w83627ehf",
"w83627dhg",
"nct6775",
"nct6776",
static unsigned short force_id;
module_param(force_id, ushort, 0);
MODULE_PARM_DESC(force_id, "Override the detected device ID");
static unsigned short fan_debounce;
module_param(fan_debounce, ushort, 0);
MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
#define DRVNAME "w83627ehf"
* Super-I/O constants and functions
#define W83627EHF_LD_HWM 0x0b
#define W83667HG_LD_VID 0x0d
#define SIO_REG_LDSEL 0x07 /* Logical device select */
#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
#define SIO_REG_EN_VRM10 0x2C /* GPIO3, GPIO4 selection */
#define SIO_REG_ENABLE 0x30 /* Logical device enable */
#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
#define SIO_REG_VID_CTRL 0xF0 /* VID control */
#define SIO_REG_VID_DATA 0xF1 /* VID data */
#define SIO_W83627EHF_ID 0x8850
#define SIO_W83627EHG_ID 0x8860
#define SIO_W83627DHG_ID 0xa020
#define SIO_W83627DHG_P_ID 0xb070
#define SIO_W83667HG_ID 0xa510
#define SIO_W83667HG_B_ID 0xb350
#define SIO_NCT6775_ID 0xb470
#define SIO_NCT6776_ID 0xc330
#define SIO_ID_MASK 0xFFF0
static inline void
superio_outb(int ioreg, int reg, int val)
outb(reg, ioreg);
outb(val, ioreg + 1);
}
static inline int
superio_inb(int ioreg, int reg)
outb(reg, ioreg);
return inb(ioreg + 1);
}
static inline void
superio_select(int ioreg, int ld)
outb(SIO_REG_LDSEL, ioreg);
outb(ld, ioreg + 1);
}
static inline void
outb(0x87, ioreg);
outb(0x87, ioreg);
}
static inline void
outb(0x02, ioreg);
outb(0x02, ioreg + 1);
}
/*
* ISA constants
*/
#define IOREGION_ALIGNMENT (~7)
#define IOREGION_OFFSET 5
#define IOREGION_LENGTH 2
#define ADDR_REG_OFFSET 0
#define DATA_REG_OFFSET 1
#define W83627EHF_REG_BANK 0x4E
#define W83627EHF_REG_CONFIG 0x40
/* Not currently used:
* REG_MAN_ID has the value 0x5ca3 for all supported chips.
* REG_CHIP_ID == 0x88/0xa1/0xc1 depending on chip model.
* REG_MAN_ID is at port 0x4f
* REG_CHIP_ID is at port 0x58 */
static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 };
static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c };
/* The W83627EHF registers for nr=7,8,9 are in bank 5 */
#define W83627EHF_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \
(0x554 + (((nr) - 7) * 2)))
#define W83627EHF_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \
(0x555 + (((nr) - 7) * 2)))
#define W83627EHF_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \
(0x550 + (nr) - 7))
static const u16 W83627EHF_REG_TEMP[] = { 0x27, 0x150, 0x250, 0x7e };
static const u16 W83627EHF_REG_TEMP_HYST[] = { 0x3a, 0x153, 0x253, 0 };
static const u16 W83627EHF_REG_TEMP_OVER[] = { 0x39, 0x155, 0x255, 0 };
static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0, 0x152, 0x252, 0 };
/* Fan clock dividers are spread over the following five registers */
#define W83627EHF_REG_FANDIV1 0x47
#define W83627EHF_REG_FANDIV2 0x4B
#define W83627EHF_REG_VBAT 0x5D
#define W83627EHF_REG_DIODE 0x59
#define W83627EHF_REG_SMI_OVT 0x4C
/* NCT6775F has its own fan divider registers */
#define NCT6775_REG_FANDIV1 0x506
#define NCT6775_REG_FANDIV2 0x507
#define NCT6775_REG_FAN_DEBOUNCE 0xf0
#define W83627EHF_REG_ALARM1 0x459
#define W83627EHF_REG_ALARM2 0x45A
#define W83627EHF_REG_ALARM3 0x45B
#define W83627EHF_REG_CASEOPEN_DET 0x42 /* SMI STATUS #2 */
#define W83627EHF_REG_CASEOPEN_CLR 0x46 /* SMI MASK #3 */
/* SmartFan registers */
#define W83627EHF_REG_FAN_STEPUP_TIME 0x0f
#define W83627EHF_REG_FAN_STEPDOWN_TIME 0x0e
/* DC or PWM output fan configuration */
static const u8 W83627EHF_REG_PWM_ENABLE[] = {
0x04, /* SYS FAN0 output mode and PWM mode */
0x04, /* CPU FAN0 output mode and PWM mode */
0x12, /* AUX FAN mode */
};
static const u8 W83627EHF_PWM_MODE_SHIFT[] = { 0, 1, 0, 6 };
static const u8 W83627EHF_PWM_ENABLE_SHIFT[] = { 2, 4, 1, 4 };
/* FAN Duty Cycle, be used to control */
Guenter Roeck
committed
static const u16 W83627EHF_REG_PWM[] = { 0x01, 0x03, 0x11, 0x61 };
static const u16 W83627EHF_REG_TARGET[] = { 0x05, 0x06, 0x13, 0x63 };
static const u8 W83627EHF_REG_TOLERANCE[] = { 0x07, 0x07, 0x14, 0x62 };
/* Advanced Fan control, some values are common for all fans */
Guenter Roeck
committed
static const u16 W83627EHF_REG_FAN_START_OUTPUT[] = { 0x0a, 0x0b, 0x16, 0x65 };
static const u16 W83627EHF_REG_FAN_STOP_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 };
static const u16 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0c, 0x0d, 0x17, 0x66 };
Guenter Roeck
committed
static const u16 W83627EHF_REG_FAN_MAX_OUTPUT_COMMON[]
= { 0xff, 0x67, 0xff, 0x69 };
Guenter Roeck
committed
static const u16 W83627EHF_REG_FAN_STEP_OUTPUT_COMMON[]
= { 0xff, 0x68, 0xff, 0x6a };
Guenter Roeck
committed
static const u16 W83627EHF_REG_FAN_MAX_OUTPUT_W83667_B[] = { 0x67, 0x69, 0x6b };
static const u16 W83627EHF_REG_FAN_STEP_OUTPUT_W83667_B[]
= { 0x68, 0x6a, 0x6c };
static const u16 NCT6775_REG_TARGET[] = { 0x101, 0x201, 0x301 };
static const u16 NCT6775_REG_FAN_MODE[] = { 0x102, 0x202, 0x302 };
static const u16 NCT6775_REG_FAN_STOP_OUTPUT[] = { 0x105, 0x205, 0x305 };
static const u16 NCT6775_REG_FAN_START_OUTPUT[] = { 0x106, 0x206, 0x306 };
static const u16 NCT6775_REG_FAN_STOP_TIME[] = { 0x107, 0x207, 0x307 };
static const u16 NCT6775_REG_PWM[] = { 0x109, 0x209, 0x309 };
static const u16 NCT6775_REG_FAN_MAX_OUTPUT[] = { 0x10a, 0x20a, 0x30a };
static const u16 NCT6775_REG_FAN_STEP_OUTPUT[] = { 0x10b, 0x20b, 0x30b };
static const u16 NCT6775_REG_FAN[] = { 0x630, 0x632, 0x634, 0x636, 0x638 };
static const u16 NCT6776_REG_FAN_MIN[] = { 0x63a, 0x63c, 0x63e, 0x640, 0x642};
static const u16 NCT6775_REG_TEMP[]
= { 0x27, 0x150, 0x250, 0x73, 0x75, 0x77, 0x62b, 0x62c, 0x62d };
static const u16 NCT6775_REG_TEMP_CONFIG[]
= { 0, 0x152, 0x252, 0, 0, 0, 0x628, 0x629, 0x62A };
static const u16 NCT6775_REG_TEMP_HYST[]
= { 0x3a, 0x153, 0x253, 0, 0, 0, 0x673, 0x678, 0x67D };
static const u16 NCT6775_REG_TEMP_OVER[]
= { 0x39, 0x155, 0x255, 0, 0, 0, 0x672, 0x677, 0x67C };
static const u16 NCT6775_REG_TEMP_SOURCE[]
= { 0x621, 0x622, 0x623, 0x100, 0x200, 0x300, 0x624, 0x625, 0x626 };
static const char *const w83667hg_b_temp_label[] = {
"SYSTIN",
"CPUTIN",
"AUXTIN",
"AMDTSI",
"PECI Agent 1",
"PECI Agent 2",
"PECI Agent 3",
"PECI Agent 4"
};
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
static const char *const nct6775_temp_label[] = {
"",
"SYSTIN",
"CPUTIN",
"AUXTIN",
"AMD SB-TSI",
"PECI Agent 0",
"PECI Agent 1",
"PECI Agent 2",
"PECI Agent 3",
"PECI Agent 4",
"PECI Agent 5",
"PECI Agent 6",
"PECI Agent 7",
"PCH_CHIP_CPU_MAX_TEMP",
"PCH_CHIP_TEMP",
"PCH_CPU_TEMP",
"PCH_MCH_TEMP",
"PCH_DIM0_TEMP",
"PCH_DIM1_TEMP",
"PCH_DIM2_TEMP",
"PCH_DIM3_TEMP"
};
static const char *const nct6776_temp_label[] = {
"",
"SYSTIN",
"CPUTIN",
"AUXTIN",
"SMBUSMASTER 0",
"SMBUSMASTER 1",
"SMBUSMASTER 2",
"SMBUSMASTER 3",
"SMBUSMASTER 4",
"SMBUSMASTER 5",
"SMBUSMASTER 6",
"SMBUSMASTER 7",
"PECI Agent 0",
"PECI Agent 1",
"PCH_CHIP_CPU_MAX_TEMP",
"PCH_CHIP_TEMP",
"PCH_CPU_TEMP",
"PCH_MCH_TEMP",
"PCH_DIM0_TEMP",
"PCH_DIM1_TEMP",
"PCH_DIM2_TEMP",
"PCH_DIM3_TEMP",
"BYTE_TEMP"
};
#define NUM_REG_TEMP ARRAY_SIZE(NCT6775_REG_TEMP)
Guenter Roeck
committed
static inline int is_word_sized(u16 reg)
{
return ((((reg & 0xff00) == 0x100
Guenter Roeck
committed
|| (reg & 0xff00) == 0x200)
&& ((reg & 0x00ff) == 0x50
|| (reg & 0x00ff) == 0x53
|| (reg & 0x00ff) == 0x55))
|| (reg & 0xfff0) == 0x630
|| reg == 0x640 || reg == 0x642
|| ((reg & 0xfff0) == 0x650
&& (reg & 0x000f) >= 0x06)
|| reg == 0x73 || reg == 0x75 || reg == 0x77
);
Guenter Roeck
committed
}
/*
* Conversions
*/
/* 1 is PWM mode, output in ms */
static inline unsigned int step_time_from_reg(u8 reg, u8 mode)
{
return mode ? 100 * reg : 400 * reg;
}
static inline u8 step_time_to_reg(unsigned int msec, u8 mode)
{
return SENSORS_LIMIT((mode ? (msec + 50) / 100 :
(msec + 200) / 400), 1, 255);
}
static unsigned int fan_from_reg8(u16 reg, unsigned int divreg)
if (reg == 0 || reg == 255)
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
return 1350000U / (reg << divreg);
}
static unsigned int fan_from_reg13(u16 reg, unsigned int divreg)
{
if ((reg & 0xff1f) == 0xff1f)
return 0;
reg = (reg & 0x1f) | ((reg & 0xff00) >> 3);
if (reg == 0)
return 0;
return 1350000U / reg;
}
static unsigned int fan_from_reg16(u16 reg, unsigned int divreg)
{
if (reg == 0 || reg == 0xffff)
return 0;
/*
* Even though the registers are 16 bit wide, the fan divisor
* still applies.
*/
return 1350000U / (reg << divreg);
}
static inline unsigned int
div_from_reg(u8 reg)
{
return 1 << reg;
}
static inline int
Guenter Roeck
committed
temp_from_reg(u16 reg, s16 regval)
Guenter Roeck
committed
if (is_word_sized(reg))
return LM75_TEMP_FROM_REG(regval);
return ((s8)regval) * 1000;
static inline u16
Guenter Roeck
committed
temp_to_reg(u16 reg, long temp)
Guenter Roeck
committed
if (is_word_sized(reg))
return LM75_TEMP_TO_REG(temp);
return (s8)DIV_ROUND_CLOSEST(SENSORS_LIMIT(temp, -127000, 128000),
1000);
/* Some of analog inputs have internal scaling (2x), 8mV is ADC LSB */
static u8 scale_in[10] = { 8, 8, 16, 16, 8, 8, 8, 16, 16, 8 };
static inline long in_from_reg(u8 reg, u8 nr)
{
return reg * scale_in[nr];
}
static inline u8 in_to_reg(u32 val, u8 nr)
{
return SENSORS_LIMIT(((val + (scale_in[nr] / 2)) / scale_in[nr]), 0,
255);
/*
* Data structures and manipulation thereof
*/
struct w83627ehf_data {
int addr; /* IO base of hw monitor block */
const char *name;
u16 reg_temp[NUM_REG_TEMP];
u16 reg_temp_over[NUM_REG_TEMP];
u16 reg_temp_hyst[NUM_REG_TEMP];
u16 reg_temp_config[NUM_REG_TEMP];
u8 temp_src[NUM_REG_TEMP];
const char * const *temp_label;
Guenter Roeck
committed
const u16 *REG_PWM;
const u16 *REG_TARGET;
const u16 *REG_FAN;
const u16 *REG_FAN_MIN;
const u16 *REG_FAN_START_OUTPUT;
const u16 *REG_FAN_STOP_OUTPUT;
const u16 *REG_FAN_STOP_TIME;
const u16 *REG_FAN_MAX_OUTPUT;
const u16 *REG_FAN_STEP_OUTPUT;
unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg);
unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg);
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
/* Register values */
u8 bank; /* current register bank */
u8 in_num; /* number of in inputs we have */
u8 in[10]; /* Register value */
u8 in_max[10]; /* Register value */
u8 in_min[10]; /* Register value */
unsigned int rpm[5];
u16 fan_min[5];
u8 fan_div[5];
u8 has_fan; /* some fan inputs can be disabled */
u8 has_fan_min; /* some fans don't have min register */
bool has_fan_div;
s16 temp[9];
s16 temp_max[9];
s16 temp_max_hyst[9];
u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */
u8 pwm_enable[4]; /* 1->manual
2->thermal cruise mode (also called SmartFan I)
3->fan speed cruise mode
4->variable thermal cruise (also called
Guenter Roeck
committed
SmartFan III)
5->enhanced variable thermal cruise (also called
SmartFan IV) */
u8 pwm_enable_orig[4]; /* original value of pwm_enable */
u8 pwm[4];
u8 target_temp[4];
u8 tolerance[4];
u8 fan_start_output[4]; /* minimum fan speed when spinning up */
u8 fan_stop_output[4]; /* minimum fan speed when spinning down */
u8 fan_stop_time[4]; /* time at minimum before disabling fan */
u8 fan_max_output[4]; /* maximum fan speed */
u8 fan_step_output[4]; /* rate of change output value */
u16 have_temp;
struct w83627ehf_sio_data {
int sioreg;
enum kinds kind;
};
/*
* On older chips, only registers 0x50-0x5f are banked.
* On more recent chips, all registers are banked.
* Assume that is the case and set the bank number for each access.
* Cache the bank number so it only needs to be set if it changes.
*/
static inline void w83627ehf_set_bank(struct w83627ehf_data *data, u16 reg)
u8 bank = reg >> 8;
if (data->bank != bank) {
outb_p(W83627EHF_REG_BANK, data->addr + ADDR_REG_OFFSET);
outb_p(bank, data->addr + DATA_REG_OFFSET);
data->bank = bank;
static u16 w83627ehf_read_value(struct w83627ehf_data *data, u16 reg)
{
int res, word_sized = is_word_sized(reg);
w83627ehf_set_bank(data, reg);
outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET);
res = inb_p(data->addr + DATA_REG_OFFSET);
if (word_sized) {
outb_p((reg & 0xff) + 1,
data->addr + ADDR_REG_OFFSET);
res = (res << 8) + inb_p(data->addr + DATA_REG_OFFSET);
return res;
}
static int w83627ehf_write_value(struct w83627ehf_data *data, u16 reg,
u16 value)
{
int word_sized = is_word_sized(reg);
w83627ehf_set_bank(data, reg);
outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET);
outb_p(value >> 8, data->addr + DATA_REG_OFFSET);
outb_p((reg & 0xff) + 1,
data->addr + ADDR_REG_OFFSET);
outb_p(value & 0xff, data->addr + DATA_REG_OFFSET);
return 0;
}
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
/* This function assumes that the caller holds data->update_lock */
static void nct6775_write_fan_div(struct w83627ehf_data *data, int nr)
{
u8 reg;
switch (nr) {
case 0:
reg = (w83627ehf_read_value(data, NCT6775_REG_FANDIV1) & 0x70)
| (data->fan_div[0] & 0x7);
w83627ehf_write_value(data, NCT6775_REG_FANDIV1, reg);
break;
case 1:
reg = (w83627ehf_read_value(data, NCT6775_REG_FANDIV1) & 0x7)
| ((data->fan_div[1] << 4) & 0x70);
w83627ehf_write_value(data, NCT6775_REG_FANDIV1, reg);
case 2:
reg = (w83627ehf_read_value(data, NCT6775_REG_FANDIV2) & 0x70)
| (data->fan_div[2] & 0x7);
w83627ehf_write_value(data, NCT6775_REG_FANDIV2, reg);
break;
case 3:
reg = (w83627ehf_read_value(data, NCT6775_REG_FANDIV2) & 0x7)
| ((data->fan_div[3] << 4) & 0x70);
w83627ehf_write_value(data, NCT6775_REG_FANDIV2, reg);
break;
}
}
/* This function assumes that the caller holds data->update_lock */
static void w83627ehf_write_fan_div(struct w83627ehf_data *data, int nr)
{
u8 reg;
switch (nr) {
case 0:
reg = (w83627ehf_read_value(data, W83627EHF_REG_FANDIV1) & 0xcf)
| ((data->fan_div[0] & 0x03) << 4);
/* fan5 input control bit is write only, compute the value */
reg |= (data->has_fan & (1 << 4)) ? 1 : 0;
w83627ehf_write_value(data, W83627EHF_REG_FANDIV1, reg);
reg = (w83627ehf_read_value(data, W83627EHF_REG_VBAT) & 0xdf)
| ((data->fan_div[0] & 0x04) << 3);
w83627ehf_write_value(data, W83627EHF_REG_VBAT, reg);
break;
case 1:
reg = (w83627ehf_read_value(data, W83627EHF_REG_FANDIV1) & 0x3f)
| ((data->fan_div[1] & 0x03) << 6);
/* fan5 input control bit is write only, compute the value */
reg |= (data->has_fan & (1 << 4)) ? 1 : 0;
w83627ehf_write_value(data, W83627EHF_REG_FANDIV1, reg);
reg = (w83627ehf_read_value(data, W83627EHF_REG_VBAT) & 0xbf)
| ((data->fan_div[1] & 0x04) << 4);
w83627ehf_write_value(data, W83627EHF_REG_VBAT, reg);
break;
case 2:
reg = (w83627ehf_read_value(data, W83627EHF_REG_FANDIV2) & 0x3f)
| ((data->fan_div[2] & 0x03) << 6);
w83627ehf_write_value(data, W83627EHF_REG_FANDIV2, reg);
reg = (w83627ehf_read_value(data, W83627EHF_REG_VBAT) & 0x7f)
| ((data->fan_div[2] & 0x04) << 5);
w83627ehf_write_value(data, W83627EHF_REG_VBAT, reg);
break;
case 3:
reg = (w83627ehf_read_value(data, W83627EHF_REG_DIODE) & 0xfc)
| (data->fan_div[3] & 0x03);
w83627ehf_write_value(data, W83627EHF_REG_DIODE, reg);
reg = (w83627ehf_read_value(data, W83627EHF_REG_SMI_OVT) & 0x7f)
| ((data->fan_div[3] & 0x04) << 5);
w83627ehf_write_value(data, W83627EHF_REG_SMI_OVT, reg);
break;
case 4:
reg = (w83627ehf_read_value(data, W83627EHF_REG_DIODE) & 0x73)
| ((data->fan_div[4] & 0x03) << 2)
| ((data->fan_div[4] & 0x04) << 5);
w83627ehf_write_value(data, W83627EHF_REG_DIODE, reg);
break;
}
}
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
static void w83627ehf_write_fan_div_common(struct device *dev,
struct w83627ehf_data *data, int nr)
{
struct w83627ehf_sio_data *sio_data = dev->platform_data;
if (sio_data->kind == nct6776)
; /* no dividers, do nothing */
else if (sio_data->kind == nct6775)
nct6775_write_fan_div(data, nr);
else
w83627ehf_write_fan_div(data, nr);
}
static void nct6775_update_fan_div(struct w83627ehf_data *data)
{
u8 i;
i = w83627ehf_read_value(data, NCT6775_REG_FANDIV1);
data->fan_div[0] = i & 0x7;
data->fan_div[1] = (i & 0x70) >> 4;
i = w83627ehf_read_value(data, NCT6775_REG_FANDIV2);
data->fan_div[2] = i & 0x7;
if (data->has_fan & (1<<3))
data->fan_div[3] = (i & 0x70) >> 4;
}
static void w83627ehf_update_fan_div(struct w83627ehf_data *data)
{
int i;
i = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1);
data->fan_div[0] = (i >> 4) & 0x03;
data->fan_div[1] = (i >> 6) & 0x03;
i = w83627ehf_read_value(data, W83627EHF_REG_FANDIV2);
data->fan_div[2] = (i >> 6) & 0x03;
i = w83627ehf_read_value(data, W83627EHF_REG_VBAT);
data->fan_div[0] |= (i >> 3) & 0x04;
data->fan_div[1] |= (i >> 4) & 0x04;
data->fan_div[2] |= (i >> 5) & 0x04;
if (data->has_fan & ((1 << 3) | (1 << 4))) {
i = w83627ehf_read_value(data, W83627EHF_REG_DIODE);
data->fan_div[3] = i & 0x03;
data->fan_div[4] = ((i >> 2) & 0x03)
| ((i >> 5) & 0x04);
}
if (data->has_fan & (1 << 3)) {
i = w83627ehf_read_value(data, W83627EHF_REG_SMI_OVT);
data->fan_div[3] |= (i >> 5) & 0x04;
}
}
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
static void w83627ehf_update_fan_div_common(struct device *dev,
struct w83627ehf_data *data)
{
struct w83627ehf_sio_data *sio_data = dev->platform_data;
if (sio_data->kind == nct6776)
; /* no dividers, do nothing */
else if (sio_data->kind == nct6775)
nct6775_update_fan_div(data);
else
w83627ehf_update_fan_div(data);
}
static void nct6775_update_pwm(struct w83627ehf_data *data)
{
int i;
int pwmcfg, fanmodecfg;
for (i = 0; i < data->pwm_num; i++) {
pwmcfg = w83627ehf_read_value(data,
W83627EHF_REG_PWM_ENABLE[i]);
fanmodecfg = w83627ehf_read_value(data,
NCT6775_REG_FAN_MODE[i]);
data->pwm_mode[i] =
((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1) ? 0 : 1;
data->pwm_enable[i] = ((fanmodecfg >> 4) & 7) + 1;
data->tolerance[i] = fanmodecfg & 0x0f;
data->pwm[i] = w83627ehf_read_value(data, data->REG_PWM[i]);
}
}
static void w83627ehf_update_pwm(struct w83627ehf_data *data)
{
int i;
int pwmcfg = 0, tolerance = 0; /* shut up the compiler */
for (i = 0; i < data->pwm_num; i++) {
if (!(data->has_fan & (1 << i)))
continue;
/* pwmcfg, tolerance mapped for i=0, i=1 to same reg */
if (i != 1) {
pwmcfg = w83627ehf_read_value(data,
W83627EHF_REG_PWM_ENABLE[i]);
tolerance = w83627ehf_read_value(data,
W83627EHF_REG_TOLERANCE[i]);
}
data->pwm_mode[i] =
((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1) ? 0 : 1;
data->pwm_enable[i] = ((pwmcfg >> W83627EHF_PWM_ENABLE_SHIFT[i])
& 3) + 1;
data->pwm[i] = w83627ehf_read_value(data, data->REG_PWM[i]);
data->tolerance[i] = (tolerance >> (i == 1 ? 4 : 0)) & 0x0f;
}
}
static void w83627ehf_update_pwm_common(struct device *dev,
struct w83627ehf_data *data)
{
struct w83627ehf_sio_data *sio_data = dev->platform_data;
if (sio_data->kind == nct6775 || sio_data->kind == nct6776)
nct6775_update_pwm(data);
else
w83627ehf_update_pwm(data);
}
static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
{
struct w83627ehf_data *data = dev_get_drvdata(dev);
struct w83627ehf_sio_data *sio_data = dev->platform_data;
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ/2)
|| !data->valid) {
/* Fan clock dividers */
w83627ehf_update_fan_div_common(dev, data);
/* Measured voltages and limits */
for (i = 0; i < data->in_num; i++) {
data->in[i] = w83627ehf_read_value(data,
data->in_min[i] = w83627ehf_read_value(data,
data->in_max[i] = w83627ehf_read_value(data,
W83627EHF_REG_IN_MAX(i));
}
/* Measured fan speeds and limits */
for (i = 0; i < 5; i++) {
if (!(data->has_fan & (1 << i)))
continue;
reg = w83627ehf_read_value(data, data->REG_FAN[i]);
data->rpm[i] = data->fan_from_reg(reg,
data->fan_div[i]);
if (data->has_fan_min & (1 << i))
data->fan_min[i] = w83627ehf_read_value(data,
Guenter Roeck
committed
data->REG_FAN_MIN[i]);
/* If we failed to measure the fan speed and clock
divider can be increased, let's try that for next
time */
if (data->has_fan_div
&& (reg >= 0xff || (sio_data->kind == nct6775
&& reg == 0x00))
&& data->fan_div[i] < 0x07) {
dev_dbg(dev, "Increasing fan%d "
"clock divider from %u to %u\n",
i + 1, div_from_reg(data->fan_div[i]),
div_from_reg(data->fan_div[i] + 1));
data->fan_div[i]++;
w83627ehf_write_fan_div_common(dev, data, i);
/* Preserve min limit if possible */
if ((data->has_fan_min & (1 << i))
&& data->fan_min[i] >= 2
&& data->fan_min[i] != 255)
w83627ehf_write_value(data,
Guenter Roeck
committed
data->REG_FAN_MIN[i],
(data->fan_min[i] /= 2));
}
}
w83627ehf_update_pwm_common(dev, data);
for (i = 0; i < data->pwm_num; i++) {
if (!(data->has_fan & (1 << i)))
continue;
data->fan_start_output[i] =
w83627ehf_read_value(data,
data->REG_FAN_START_OUTPUT[i]);
data->fan_stop_output[i] =
w83627ehf_read_value(data,
data->REG_FAN_STOP_OUTPUT[i]);
data->fan_stop_time[i] =
w83627ehf_read_value(data,
data->REG_FAN_STOP_TIME[i]);
if (data->REG_FAN_MAX_OUTPUT &&
data->REG_FAN_MAX_OUTPUT[i] != 0xff)
data->fan_max_output[i] =
w83627ehf_read_value(data,
data->REG_FAN_MAX_OUTPUT[i]);
if (data->REG_FAN_STEP_OUTPUT &&
data->REG_FAN_STEP_OUTPUT[i] != 0xff)
data->fan_step_output[i] =
w83627ehf_read_value(data,
data->REG_FAN_STEP_OUTPUT[i]);
data->target_temp[i] =
w83627ehf_read_value(data,
Guenter Roeck
committed
data->REG_TARGET[i]) &
(data->pwm_mode[i] == 1 ? 0x7f : 0xff);
}
/* Measured temperatures and limits */
for (i = 0; i < NUM_REG_TEMP; i++) {
if (!(data->have_temp & (1 << i)))
continue;
data->temp[i] = w83627ehf_read_value(data,
data->reg_temp[i]);
if (data->reg_temp_over[i])
data->temp_max[i]
= w83627ehf_read_value(data,
data->reg_temp_over[i]);
if (data->reg_temp_hyst[i])
data->temp_max_hyst[i]
= w83627ehf_read_value(data,
data->reg_temp_hyst[i]);
data->alarms = w83627ehf_read_value(data,
(w83627ehf_read_value(data,
(w83627ehf_read_value(data,
data->caseopen = w83627ehf_read_value(data,
W83627EHF_REG_CASEOPEN_DET);
data->last_updated = jiffies;
data->valid = 1;
}
mutex_unlock(&data->update_lock);
return data;
}
/*
* Sysfs callback functions
*/
#define show_in_reg(reg) \
static ssize_t \
show_##reg(struct device *dev, struct device_attribute *attr, \
char *buf) \
{ \
struct w83627ehf_data *data = w83627ehf_update_device(dev); \
struct sensor_device_attribute *sensor_attr = \
to_sensor_dev_attr(attr); \
int nr = sensor_attr->index; \
return sprintf(buf, "%ld\n", in_from_reg(data->reg[nr], nr)); \
}
show_in_reg(in)
show_in_reg(in_min)
show_in_reg(in_max)
#define store_in_reg(REG, reg) \
static ssize_t \
store_in_##reg(struct device *dev, struct device_attribute *attr, \
const char *buf, size_t count) \
struct w83627ehf_data *data = dev_get_drvdata(dev); \
struct sensor_device_attribute *sensor_attr = \
to_sensor_dev_attr(attr); \
int nr = sensor_attr->index; \
Guenter Roeck
committed
unsigned long val; \
int err; \
err = strict_strtoul(buf, 10, &val); \
if (err < 0) \
return err; \
mutex_lock(&data->update_lock); \
data->in_##reg[nr] = in_to_reg(val, nr); \
w83627ehf_write_value(data, W83627EHF_REG_IN_##REG(nr), \
data->in_##reg[nr]); \
mutex_unlock(&data->update_lock); \
return count; \
}
store_in_reg(MIN, min)
store_in_reg(MAX, max)
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct w83627ehf_data *data = w83627ehf_update_device(dev);
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
int nr = sensor_attr->index;
return sprintf(buf, "%u\n", (data->alarms >> nr) & 0x01);
}
static struct sensor_device_attribute sda_in_input[] = {
SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0),
SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1),
SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2),
SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3),
SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4),
SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5),
SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6),
SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7),
SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8),
SENSOR_ATTR(in9_input, S_IRUGO, show_in, NULL, 9),
};
static struct sensor_device_attribute sda_in_alarm[] = {
SENSOR_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0),
SENSOR_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1),
SENSOR_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2),
SENSOR_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3),
SENSOR_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8),
SENSOR_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 21),
SENSOR_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 20),
SENSOR_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 16),
SENSOR_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 17),
SENSOR_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 19),
};
static struct sensor_device_attribute sda_in_min[] = {
SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0),
SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1),
SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2),
SENSOR_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 3),
SENSOR_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 4),
SENSOR_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 5),
SENSOR_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 6),
SENSOR_ATTR(in7_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 7),
SENSOR_ATTR(in8_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 8),
SENSOR_ATTR(in9_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 9),
};
static struct sensor_device_attribute sda_in_max[] = {
SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0),
SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1),
SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2),
SENSOR_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 3),
SENSOR_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 4),
SENSOR_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 5),
SENSOR_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 6),
SENSOR_ATTR(in7_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 7),
SENSOR_ATTR(in8_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 8),
SENSOR_ATTR(in9_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 9),
static ssize_t
show_fan(struct device *dev, struct device_attribute *attr, char *buf)
{
struct w83627ehf_data *data = w83627ehf_update_device(dev);
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);