2005-04-26 04:43:25 +08:00
|
|
|
/*
|
|
|
|
w83627ehf - Driver for the hardware monitoring functionality of
|
|
|
|
the Winbond W83627EHF Super-I/O chip
|
|
|
|
Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
|
2006-09-25 03:25:52 +08:00
|
|
|
Copyright (C) 2006 Yuan Mu (Winbond),
|
2006-12-13 01:18:30 +08:00
|
|
|
Rudolf Marek <r.marek@assembler.cz>
|
2006-09-25 03:04:38 +08:00
|
|
|
David Hubbard <david.c.hubbard@gmail.com>
|
2005-04-26 04:43:25 +08:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
2005-07-28 03:33:15 +08:00
|
|
|
This driver also supports the W83627EHG, which is the lead-free
|
|
|
|
version of the W83627EHF.
|
|
|
|
|
2005-04-26 04:43:25 +08:00
|
|
|
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:
|
|
|
|
|
2006-07-06 00:14:31 +08:00
|
|
|
Chip #vin #fan #pwm #temp chip_id man_id
|
|
|
|
w83627ehf 10 5 4 3 0x88,0xa1 0x5ca3
|
2005-04-26 04:43:25 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/i2c.h>
|
[PATCH] I2C: Separate non-i2c hwmon drivers from i2c-core (3/9)
Convert the 10 ISA hardware monitoring drivers (it87, lm78, pc87360,
sis5595, smsc47b397, smsc47m1, via686a, w83627hf, w83627ehf, w83781d) to
explicitely register with i2c-isa. For hybrid drivers (it87, lm78,
w83781d), we now have two separate instances of i2c_driver, one for the
I2C interface of the chip, and one for ISA interface. In the long run,
the one for ISA will be replaced with a different driver type.
At this point, all drivers are working again, except for missing
dependencies in Kconfig.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-07-20 05:51:07 +08:00
|
|
|
#include <linux/i2c-isa.h>
|
2005-07-16 09:39:18 +08:00
|
|
|
#include <linux/hwmon.h>
|
2006-02-06 06:24:16 +08:00
|
|
|
#include <linux/hwmon-sysfs.h>
|
2005-07-16 09:39:18 +08:00
|
|
|
#include <linux/err.h>
|
2006-01-19 06:19:26 +08:00
|
|
|
#include <linux/mutex.h>
|
2005-04-26 04:43:25 +08:00
|
|
|
#include <asm/io.h>
|
|
|
|
#include "lm75.h"
|
|
|
|
|
[PATCH] I2C: Separate non-i2c hwmon drivers from i2c-core (5/9)
Call the ISA chip drivers detection function directly instead of relying
on i2c_detect. The net effect is that address lists won't be handled
anymore, but they were mostly useless in the ISA case anyway (pc87360,
smsc47m1, smsc47b397 had already dropped them).
We don't need to handle multiple devices, all we may need is a way to
force a given address instead of the original one (some drivers already
do: sis5595, via686a, w83627hf), and, for drivers supporting multiple
chips, a way to force one given kind. All this may be added later on
demand, but I actually don't think there will be much demand.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-07-20 05:56:35 +08:00
|
|
|
/* The actual ISA address is read from Super-I/O configuration space */
|
|
|
|
static unsigned short address;
|
2005-04-26 04:43:25 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Super-I/O constants and functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int REG; /* The register to read/write */
|
|
|
|
static int VAL; /* The value to read/write */
|
|
|
|
|
|
|
|
#define W83627EHF_LD_HWM 0x0b
|
|
|
|
|
|
|
|
#define SIO_REG_LDSEL 0x07 /* Logical device select */
|
|
|
|
#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
|
|
|
|
#define SIO_REG_ENABLE 0x30 /* Logical device enable */
|
|
|
|
#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
|
|
|
|
|
|
|
|
#define SIO_W83627EHF_ID 0x8840
|
|
|
|
#define SIO_ID_MASK 0xFFC0
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
superio_outb(int reg, int val)
|
|
|
|
{
|
|
|
|
outb(reg, REG);
|
|
|
|
outb(val, VAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
superio_inb(int reg)
|
|
|
|
{
|
|
|
|
outb(reg, REG);
|
|
|
|
return inb(VAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
superio_select(int ld)
|
|
|
|
{
|
|
|
|
outb(SIO_REG_LDSEL, REG);
|
|
|
|
outb(ld, VAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
superio_enter(void)
|
|
|
|
{
|
|
|
|
outb(0x87, REG);
|
|
|
|
outb(0x87, REG);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
superio_exit(void)
|
|
|
|
{
|
|
|
|
outb(0x02, REG);
|
|
|
|
outb(0x02, VAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ISA constants
|
|
|
|
*/
|
|
|
|
|
2005-10-08 05:11:03 +08:00
|
|
|
#define REGION_ALIGNMENT ~7
|
|
|
|
#define REGION_OFFSET 5
|
|
|
|
#define REGION_LENGTH 2
|
2005-04-26 04:43:25 +08:00
|
|
|
#define ADDR_REG_OFFSET 5
|
|
|
|
#define DATA_REG_OFFSET 6
|
|
|
|
|
|
|
|
#define W83627EHF_REG_BANK 0x4E
|
|
|
|
#define W83627EHF_REG_CONFIG 0x40
|
|
|
|
#define W83627EHF_REG_CHIP_ID 0x49
|
|
|
|
#define W83627EHF_REG_MAN_ID 0x4F
|
|
|
|
|
|
|
|
static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 };
|
|
|
|
static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c };
|
|
|
|
|
2006-03-23 23:25:22 +08:00
|
|
|
/* 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))
|
|
|
|
|
2005-04-26 04:43:25 +08:00
|
|
|
#define W83627EHF_REG_TEMP1 0x27
|
|
|
|
#define W83627EHF_REG_TEMP1_HYST 0x3a
|
|
|
|
#define W83627EHF_REG_TEMP1_OVER 0x39
|
|
|
|
static const u16 W83627EHF_REG_TEMP[] = { 0x150, 0x250 };
|
|
|
|
static const u16 W83627EHF_REG_TEMP_HYST[] = { 0x153, 0x253 };
|
|
|
|
static const u16 W83627EHF_REG_TEMP_OVER[] = { 0x155, 0x255 };
|
|
|
|
static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 };
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
|
2006-03-23 23:30:29 +08:00
|
|
|
#define W83627EHF_REG_ALARM1 0x459
|
|
|
|
#define W83627EHF_REG_ALARM2 0x45A
|
|
|
|
#define W83627EHF_REG_ALARM3 0x45B
|
|
|
|
|
2006-07-06 00:14:31 +08:00
|
|
|
/* SmartFan registers */
|
|
|
|
/* 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 */
|
|
|
|
0x62, /* CPU fan1 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 */
|
|
|
|
static const u8 W83627EHF_REG_PWM[] = { 0x01, 0x03, 0x11, 0x61 };
|
|
|
|
static const u8 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 */
|
|
|
|
static const u8 W83627EHF_REG_FAN_MIN_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 };
|
|
|
|
static const u8 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0C, 0x0D, 0x17, 0x66 };
|
|
|
|
|
2005-04-26 04:43:25 +08:00
|
|
|
/*
|
|
|
|
* Conversions
|
|
|
|
*/
|
|
|
|
|
2006-07-06 00:14:31 +08:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2005-04-26 04:43:25 +08:00
|
|
|
static inline unsigned int
|
|
|
|
fan_from_reg(u8 reg, unsigned int div)
|
|
|
|
{
|
|
|
|
if (reg == 0 || reg == 255)
|
|
|
|
return 0;
|
|
|
|
return 1350000U / (reg * div);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned int
|
|
|
|
div_from_reg(u8 reg)
|
|
|
|
{
|
|
|
|
return 1 << reg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
temp1_from_reg(s8 reg)
|
|
|
|
{
|
|
|
|
return reg * 1000;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline s8
|
2006-07-06 00:14:31 +08:00
|
|
|
temp1_to_reg(int temp, int min, int max)
|
2005-04-26 04:43:25 +08:00
|
|
|
{
|
2006-07-06 00:14:31 +08:00
|
|
|
if (temp <= min)
|
|
|
|
return min / 1000;
|
|
|
|
if (temp >= max)
|
|
|
|
return max / 1000;
|
2005-04-26 04:43:25 +08:00
|
|
|
if (temp < 0)
|
|
|
|
return (temp - 500) / 1000;
|
|
|
|
return (temp + 500) / 1000;
|
|
|
|
}
|
|
|
|
|
2006-03-23 23:25:22 +08:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2005-04-26 04:43:25 +08:00
|
|
|
/*
|
|
|
|
* Data structures and manipulation thereof
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct w83627ehf_data {
|
|
|
|
struct i2c_client client;
|
2005-07-16 09:39:18 +08:00
|
|
|
struct class_device *class_dev;
|
2006-01-19 06:19:26 +08:00
|
|
|
struct mutex lock;
|
2005-04-26 04:43:25 +08:00
|
|
|
|
2006-01-19 06:19:26 +08:00
|
|
|
struct mutex update_lock;
|
2005-04-26 04:43:25 +08:00
|
|
|
char valid; /* !=0 if following fields are valid */
|
|
|
|
unsigned long last_updated; /* In jiffies */
|
|
|
|
|
|
|
|
/* Register values */
|
2006-03-23 23:25:22 +08:00
|
|
|
u8 in[10]; /* Register value */
|
|
|
|
u8 in_max[10]; /* Register value */
|
|
|
|
u8 in_min[10]; /* Register value */
|
2005-04-26 04:43:25 +08:00
|
|
|
u8 fan[5];
|
|
|
|
u8 fan_min[5];
|
|
|
|
u8 fan_div[5];
|
|
|
|
u8 has_fan; /* some fan inputs can be disabled */
|
|
|
|
s8 temp1;
|
|
|
|
s8 temp1_max;
|
|
|
|
s8 temp1_max_hyst;
|
|
|
|
s16 temp[2];
|
|
|
|
s16 temp_max[2];
|
|
|
|
s16 temp_max_hyst[2];
|
2006-03-23 23:30:29 +08:00
|
|
|
u32 alarms;
|
2006-07-06 00:14:31 +08:00
|
|
|
|
|
|
|
u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */
|
|
|
|
u8 pwm_enable[4]; /* 1->manual
|
|
|
|
2->thermal cruise (also called SmartFan I) */
|
|
|
|
u8 pwm[4];
|
|
|
|
u8 target_temp[4];
|
|
|
|
u8 tolerance[4];
|
|
|
|
|
|
|
|
u8 fan_min_output[4]; /* minimum fan speed */
|
|
|
|
u8 fan_stop_time[4];
|
2005-04-26 04:43:25 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static inline int is_word_sized(u16 reg)
|
|
|
|
{
|
|
|
|
return (((reg & 0xff00) == 0x100
|
|
|
|
|| (reg & 0xff00) == 0x200)
|
|
|
|
&& ((reg & 0x00ff) == 0x50
|
|
|
|
|| (reg & 0x00ff) == 0x53
|
|
|
|
|| (reg & 0x00ff) == 0x55));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We assume that the default bank is 0, thus the following two functions do
|
|
|
|
nothing for registers which live in bank 0. For others, they respectively
|
|
|
|
set the bank register to the correct value (before the register is
|
|
|
|
accessed), and back to 0 (afterwards). */
|
|
|
|
static inline void w83627ehf_set_bank(struct i2c_client *client, u16 reg)
|
|
|
|
{
|
|
|
|
if (reg & 0xff00) {
|
|
|
|
outb_p(W83627EHF_REG_BANK, client->addr + ADDR_REG_OFFSET);
|
|
|
|
outb_p(reg >> 8, client->addr + DATA_REG_OFFSET);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void w83627ehf_reset_bank(struct i2c_client *client, u16 reg)
|
|
|
|
{
|
|
|
|
if (reg & 0xff00) {
|
|
|
|
outb_p(W83627EHF_REG_BANK, client->addr + ADDR_REG_OFFSET);
|
|
|
|
outb_p(0, client->addr + DATA_REG_OFFSET);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static u16 w83627ehf_read_value(struct i2c_client *client, u16 reg)
|
|
|
|
{
|
|
|
|
struct w83627ehf_data *data = i2c_get_clientdata(client);
|
|
|
|
int res, word_sized = is_word_sized(reg);
|
|
|
|
|
2006-01-19 06:19:26 +08:00
|
|
|
mutex_lock(&data->lock);
|
2005-04-26 04:43:25 +08:00
|
|
|
|
|
|
|
w83627ehf_set_bank(client, reg);
|
|
|
|
outb_p(reg & 0xff, client->addr + ADDR_REG_OFFSET);
|
|
|
|
res = inb_p(client->addr + DATA_REG_OFFSET);
|
|
|
|
if (word_sized) {
|
|
|
|
outb_p((reg & 0xff) + 1,
|
|
|
|
client->addr + ADDR_REG_OFFSET);
|
|
|
|
res = (res << 8) + inb_p(client->addr + DATA_REG_OFFSET);
|
|
|
|
}
|
|
|
|
w83627ehf_reset_bank(client, reg);
|
|
|
|
|
2006-01-19 06:19:26 +08:00
|
|
|
mutex_unlock(&data->lock);
|
2005-04-26 04:43:25 +08:00
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int w83627ehf_write_value(struct i2c_client *client, u16 reg, u16 value)
|
|
|
|
{
|
|
|
|
struct w83627ehf_data *data = i2c_get_clientdata(client);
|
|
|
|
int word_sized = is_word_sized(reg);
|
|
|
|
|
2006-01-19 06:19:26 +08:00
|
|
|
mutex_lock(&data->lock);
|
2005-04-26 04:43:25 +08:00
|
|
|
|
|
|
|
w83627ehf_set_bank(client, reg);
|
|
|
|
outb_p(reg & 0xff, client->addr + ADDR_REG_OFFSET);
|
|
|
|
if (word_sized) {
|
|
|
|
outb_p(value >> 8, client->addr + DATA_REG_OFFSET);
|
|
|
|
outb_p((reg & 0xff) + 1,
|
|
|
|
client->addr + ADDR_REG_OFFSET);
|
|
|
|
}
|
|
|
|
outb_p(value & 0xff, client->addr + DATA_REG_OFFSET);
|
|
|
|
w83627ehf_reset_bank(client, reg);
|
|
|
|
|
2006-01-19 06:19:26 +08:00
|
|
|
mutex_unlock(&data->lock);
|
2005-04-26 04:43:25 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This function assumes that the caller holds data->update_lock */
|
|
|
|
static void w83627ehf_write_fan_div(struct i2c_client *client, int nr)
|
|
|
|
{
|
|
|
|
struct w83627ehf_data *data = i2c_get_clientdata(client);
|
|
|
|
u8 reg;
|
|
|
|
|
|
|
|
switch (nr) {
|
|
|
|
case 0:
|
|
|
|
reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV1) & 0xcf)
|
|
|
|
| ((data->fan_div[0] & 0x03) << 4);
|
2006-10-09 04:02:09 +08:00
|
|
|
/* fan5 input control bit is write only, compute the value */
|
|
|
|
reg |= (data->has_fan & (1 << 4)) ? 1 : 0;
|
2005-04-26 04:43:25 +08:00
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_FANDIV1, reg);
|
|
|
|
reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0xdf)
|
|
|
|
| ((data->fan_div[0] & 0x04) << 3);
|
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV1) & 0x3f)
|
|
|
|
| ((data->fan_div[1] & 0x03) << 6);
|
2006-10-09 04:02:09 +08:00
|
|
|
/* fan5 input control bit is write only, compute the value */
|
|
|
|
reg |= (data->has_fan & (1 << 4)) ? 1 : 0;
|
2005-04-26 04:43:25 +08:00
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_FANDIV1, reg);
|
|
|
|
reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0xbf)
|
|
|
|
| ((data->fan_div[1] & 0x04) << 4);
|
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV2) & 0x3f)
|
|
|
|
| ((data->fan_div[2] & 0x03) << 6);
|
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_FANDIV2, reg);
|
|
|
|
reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0x7f)
|
|
|
|
| ((data->fan_div[2] & 0x04) << 5);
|
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
reg = (w83627ehf_read_value(client, W83627EHF_REG_DIODE) & 0xfc)
|
|
|
|
| (data->fan_div[3] & 0x03);
|
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_DIODE, reg);
|
|
|
|
reg = (w83627ehf_read_value(client, W83627EHF_REG_SMI_OVT) & 0x7f)
|
|
|
|
| ((data->fan_div[3] & 0x04) << 5);
|
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_SMI_OVT, reg);
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
reg = (w83627ehf_read_value(client, W83627EHF_REG_DIODE) & 0x73)
|
|
|
|
| ((data->fan_div[4] & 0x03) << 3)
|
|
|
|
| ((data->fan_div[4] & 0x04) << 5);
|
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_DIODE, reg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
|
|
|
|
{
|
|
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
|
|
struct w83627ehf_data *data = i2c_get_clientdata(client);
|
2006-07-06 00:14:31 +08:00
|
|
|
int pwmcfg = 0, tolerance = 0; /* shut up the compiler */
|
2005-04-26 04:43:25 +08:00
|
|
|
int i;
|
|
|
|
|
2006-01-19 06:19:26 +08:00
|
|
|
mutex_lock(&data->update_lock);
|
2005-04-26 04:43:25 +08:00
|
|
|
|
|
|
|
if (time_after(jiffies, data->last_updated + HZ)
|
|
|
|
|| !data->valid) {
|
|
|
|
/* Fan clock dividers */
|
|
|
|
i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1);
|
|
|
|
data->fan_div[0] = (i >> 4) & 0x03;
|
|
|
|
data->fan_div[1] = (i >> 6) & 0x03;
|
|
|
|
i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV2);
|
|
|
|
data->fan_div[2] = (i >> 6) & 0x03;
|
|
|
|
i = w83627ehf_read_value(client, 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(client, 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(client, W83627EHF_REG_SMI_OVT);
|
|
|
|
data->fan_div[3] |= (i >> 5) & 0x04;
|
|
|
|
}
|
|
|
|
|
2006-03-23 23:25:22 +08:00
|
|
|
/* Measured voltages and limits */
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
|
|
data->in[i] = w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_IN(i));
|
|
|
|
data->in_min[i] = w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_IN_MIN(i));
|
|
|
|
data->in_max[i] = w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_IN_MAX(i));
|
|
|
|
}
|
|
|
|
|
2005-04-26 04:43:25 +08:00
|
|
|
/* Measured fan speeds and limits */
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
|
|
if (!(data->has_fan & (1 << i)))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
data->fan[i] = w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_FAN[i]);
|
|
|
|
data->fan_min[i] = w83627ehf_read_value(client,
|
|
|
|
W83627EHF_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->fan[i] == 0xff
|
|
|
|
&& data->fan_div[i] < 0x07) {
|
|
|
|
dev_dbg(&client->dev, "Increasing fan %d "
|
|
|
|
"clock divider from %u to %u\n",
|
|
|
|
i, div_from_reg(data->fan_div[i]),
|
|
|
|
div_from_reg(data->fan_div[i] + 1));
|
|
|
|
data->fan_div[i]++;
|
|
|
|
w83627ehf_write_fan_div(client, i);
|
|
|
|
/* Preserve min limit if possible */
|
|
|
|
if (data->fan_min[i] >= 2
|
|
|
|
&& data->fan_min[i] != 255)
|
|
|
|
w83627ehf_write_value(client,
|
|
|
|
W83627EHF_REG_FAN_MIN[i],
|
|
|
|
(data->fan_min[i] /= 2));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-06 00:14:31 +08:00
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
/* pwmcfg, tolarance mapped for i=0, i=1 to same reg */
|
|
|
|
if (i != 1) {
|
|
|
|
pwmcfg = w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_PWM_ENABLE[i]);
|
|
|
|
tolerance = w83627ehf_read_value(client,
|
|
|
|
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(client,
|
|
|
|
W83627EHF_REG_PWM[i]);
|
|
|
|
data->fan_min_output[i] = w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_FAN_MIN_OUTPUT[i]);
|
|
|
|
data->fan_stop_time[i] = w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_FAN_STOP_TIME[i]);
|
|
|
|
data->target_temp[i] =
|
|
|
|
w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_TARGET[i]) &
|
|
|
|
(data->pwm_mode[i] == 1 ? 0x7f : 0xff);
|
|
|
|
data->tolerance[i] = (tolerance >> (i == 1 ? 4 : 0))
|
|
|
|
& 0x0f;
|
|
|
|
}
|
|
|
|
|
2005-04-26 04:43:25 +08:00
|
|
|
/* Measured temperatures and limits */
|
|
|
|
data->temp1 = w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_TEMP1);
|
|
|
|
data->temp1_max = w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_TEMP1_OVER);
|
|
|
|
data->temp1_max_hyst = w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_TEMP1_HYST);
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
data->temp[i] = w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_TEMP[i]);
|
|
|
|
data->temp_max[i] = w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_TEMP_OVER[i]);
|
|
|
|
data->temp_max_hyst[i] = w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_TEMP_HYST[i]);
|
|
|
|
}
|
|
|
|
|
2006-03-23 23:30:29 +08:00
|
|
|
data->alarms = w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_ALARM1) |
|
|
|
|
(w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_ALARM2) << 8) |
|
|
|
|
(w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_ALARM3) << 16);
|
|
|
|
|
2005-04-26 04:43:25 +08:00
|
|
|
data->last_updated = jiffies;
|
|
|
|
data->valid = 1;
|
|
|
|
}
|
|
|
|
|
2006-01-19 06:19:26 +08:00
|
|
|
mutex_unlock(&data->update_lock);
|
2005-04-26 04:43:25 +08:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sysfs callback functions
|
|
|
|
*/
|
2006-03-23 23:25:22 +08:00
|
|
|
#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 i2c_client *client = to_i2c_client(dev); \
|
|
|
|
struct w83627ehf_data *data = i2c_get_clientdata(client); \
|
|
|
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
|
|
|
|
int nr = sensor_attr->index; \
|
|
|
|
u32 val = simple_strtoul(buf, NULL, 10); \
|
|
|
|
\
|
|
|
|
mutex_lock(&data->update_lock); \
|
|
|
|
data->in_##reg[nr] = in_to_reg(val, nr); \
|
|
|
|
w83627ehf_write_value(client, 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)
|
|
|
|
|
2006-03-23 23:30:29 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2006-03-23 23:25:22 +08:00
|
|
|
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),
|
|
|
|
};
|
|
|
|
|
2006-03-23 23:30:29 +08:00
|
|
|
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),
|
|
|
|
};
|
|
|
|
|
2006-03-23 23:25:22 +08:00
|
|
|
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),
|
|
|
|
};
|
|
|
|
|
2005-04-26 04:43:25 +08:00
|
|
|
#define show_fan_reg(reg) \
|
|
|
|
static ssize_t \
|
2006-02-06 06:24:16 +08:00
|
|
|
show_##reg(struct device *dev, struct device_attribute *attr, \
|
|
|
|
char *buf) \
|
2005-04-26 04:43:25 +08:00
|
|
|
{ \
|
|
|
|
struct w83627ehf_data *data = w83627ehf_update_device(dev); \
|
2006-02-06 06:24:16 +08:00
|
|
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
|
|
|
|
int nr = sensor_attr->index; \
|
2005-04-26 04:43:25 +08:00
|
|
|
return sprintf(buf, "%d\n", \
|
|
|
|
fan_from_reg(data->reg[nr], \
|
|
|
|
div_from_reg(data->fan_div[nr]))); \
|
|
|
|
}
|
|
|
|
show_fan_reg(fan);
|
|
|
|
show_fan_reg(fan_min);
|
|
|
|
|
|
|
|
static ssize_t
|
2006-02-06 06:24:16 +08:00
|
|
|
show_fan_div(struct device *dev, struct device_attribute *attr,
|
|
|
|
char *buf)
|
2005-04-26 04:43:25 +08:00
|
|
|
{
|
|
|
|
struct w83627ehf_data *data = w83627ehf_update_device(dev);
|
2006-02-06 06:24:16 +08:00
|
|
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
|
|
|
int nr = sensor_attr->index;
|
|
|
|
return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr]));
|
2005-04-26 04:43:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
2006-02-06 06:24:16 +08:00
|
|
|
store_fan_min(struct device *dev, struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count)
|
2005-04-26 04:43:25 +08:00
|
|
|
{
|
|
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
|
|
struct w83627ehf_data *data = i2c_get_clientdata(client);
|
2006-02-06 06:24:16 +08:00
|
|
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
|
|
|
int nr = sensor_attr->index;
|
2005-04-26 04:43:25 +08:00
|
|
|
unsigned int val = simple_strtoul(buf, NULL, 10);
|
|
|
|
unsigned int reg;
|
|
|
|
u8 new_div;
|
|
|
|
|
2006-01-19 06:19:26 +08:00
|
|
|
mutex_lock(&data->update_lock);
|
2005-04-26 04:43:25 +08:00
|
|
|
if (!val) {
|
|
|
|
/* No min limit, alarm disabled */
|
|
|
|
data->fan_min[nr] = 255;
|
|
|
|
new_div = data->fan_div[nr]; /* No change */
|
|
|
|
dev_info(dev, "fan%u low limit and alarm disabled\n", nr + 1);
|
|
|
|
} else if ((reg = 1350000U / val) >= 128 * 255) {
|
|
|
|
/* Speed below this value cannot possibly be represented,
|
|
|
|
even with the highest divider (128) */
|
|
|
|
data->fan_min[nr] = 254;
|
|
|
|
new_div = 7; /* 128 == (1 << 7) */
|
|
|
|
dev_warn(dev, "fan%u low limit %u below minimum %u, set to "
|
|
|
|
"minimum\n", nr + 1, val, fan_from_reg(254, 128));
|
|
|
|
} else if (!reg) {
|
|
|
|
/* Speed above this value cannot possibly be represented,
|
|
|
|
even with the lowest divider (1) */
|
|
|
|
data->fan_min[nr] = 1;
|
|
|
|
new_div = 0; /* 1 == (1 << 0) */
|
|
|
|
dev_warn(dev, "fan%u low limit %u above maximum %u, set to "
|
2005-05-03 05:08:22 +08:00
|
|
|
"maximum\n", nr + 1, val, fan_from_reg(1, 1));
|
2005-04-26 04:43:25 +08:00
|
|
|
} else {
|
|
|
|
/* Automatically pick the best divider, i.e. the one such
|
|
|
|
that the min limit will correspond to a register value
|
|
|
|
in the 96..192 range */
|
|
|
|
new_div = 0;
|
|
|
|
while (reg > 192 && new_div < 7) {
|
|
|
|
reg >>= 1;
|
|
|
|
new_div++;
|
|
|
|
}
|
|
|
|
data->fan_min[nr] = reg;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write both the fan clock divider (if it changed) and the new
|
|
|
|
fan min (unconditionally) */
|
|
|
|
if (new_div != data->fan_div[nr]) {
|
|
|
|
if (new_div > data->fan_div[nr])
|
|
|
|
data->fan[nr] >>= (data->fan_div[nr] - new_div);
|
|
|
|
else
|
|
|
|
data->fan[nr] <<= (new_div - data->fan_div[nr]);
|
|
|
|
|
|
|
|
dev_dbg(dev, "fan%u clock divider changed from %u to %u\n",
|
|
|
|
nr + 1, div_from_reg(data->fan_div[nr]),
|
|
|
|
div_from_reg(new_div));
|
|
|
|
data->fan_div[nr] = new_div;
|
|
|
|
w83627ehf_write_fan_div(client, nr);
|
|
|
|
}
|
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_FAN_MIN[nr],
|
|
|
|
data->fan_min[nr]);
|
2006-01-19 06:19:26 +08:00
|
|
|
mutex_unlock(&data->update_lock);
|
2005-04-26 04:43:25 +08:00
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2006-02-06 06:24:16 +08:00
|
|
|
static struct sensor_device_attribute sda_fan_input[] = {
|
|
|
|
SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0),
|
|
|
|
SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1),
|
|
|
|
SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2),
|
|
|
|
SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3),
|
|
|
|
SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4),
|
|
|
|
};
|
2005-04-26 04:43:25 +08:00
|
|
|
|
2006-03-23 23:30:29 +08:00
|
|
|
static struct sensor_device_attribute sda_fan_alarm[] = {
|
|
|
|
SENSOR_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6),
|
|
|
|
SENSOR_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7),
|
|
|
|
SENSOR_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11),
|
|
|
|
SENSOR_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, 10),
|
|
|
|
SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 23),
|
|
|
|
};
|
|
|
|
|
2006-02-06 06:24:16 +08:00
|
|
|
static struct sensor_device_attribute sda_fan_min[] = {
|
|
|
|
SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
|
|
|
|
store_fan_min, 0),
|
|
|
|
SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min,
|
|
|
|
store_fan_min, 1),
|
|
|
|
SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min,
|
|
|
|
store_fan_min, 2),
|
|
|
|
SENSOR_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min,
|
|
|
|
store_fan_min, 3),
|
|
|
|
SENSOR_ATTR(fan5_min, S_IWUSR | S_IRUGO, show_fan_min,
|
|
|
|
store_fan_min, 4),
|
|
|
|
};
|
2005-04-26 04:43:25 +08:00
|
|
|
|
2006-02-06 06:24:16 +08:00
|
|
|
static struct sensor_device_attribute sda_fan_div[] = {
|
|
|
|
SENSOR_ATTR(fan1_div, S_IRUGO, show_fan_div, NULL, 0),
|
|
|
|
SENSOR_ATTR(fan2_div, S_IRUGO, show_fan_div, NULL, 1),
|
|
|
|
SENSOR_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2),
|
|
|
|
SENSOR_ATTR(fan4_div, S_IRUGO, show_fan_div, NULL, 3),
|
|
|
|
SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4),
|
|
|
|
};
|
|
|
|
|
2005-04-26 04:43:25 +08:00
|
|
|
#define show_temp1_reg(reg) \
|
|
|
|
static ssize_t \
|
2005-06-22 12:01:59 +08:00
|
|
|
show_##reg(struct device *dev, struct device_attribute *attr, \
|
|
|
|
char *buf) \
|
2005-04-26 04:43:25 +08:00
|
|
|
{ \
|
|
|
|
struct w83627ehf_data *data = w83627ehf_update_device(dev); \
|
|
|
|
return sprintf(buf, "%d\n", temp1_from_reg(data->reg)); \
|
|
|
|
}
|
|
|
|
show_temp1_reg(temp1);
|
|
|
|
show_temp1_reg(temp1_max);
|
|
|
|
show_temp1_reg(temp1_max_hyst);
|
|
|
|
|
|
|
|
#define store_temp1_reg(REG, reg) \
|
|
|
|
static ssize_t \
|
2005-06-22 12:01:59 +08:00
|
|
|
store_temp1_##reg(struct device *dev, struct device_attribute *attr, \
|
|
|
|
const char *buf, size_t count) \
|
2005-04-26 04:43:25 +08:00
|
|
|
{ \
|
|
|
|
struct i2c_client *client = to_i2c_client(dev); \
|
|
|
|
struct w83627ehf_data *data = i2c_get_clientdata(client); \
|
|
|
|
u32 val = simple_strtoul(buf, NULL, 10); \
|
|
|
|
\
|
2006-01-19 06:19:26 +08:00
|
|
|
mutex_lock(&data->update_lock); \
|
2006-07-06 00:14:31 +08:00
|
|
|
data->temp1_##reg = temp1_to_reg(val, -128000, 127000); \
|
2005-04-26 04:43:25 +08:00
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_TEMP1_##REG, \
|
|
|
|
data->temp1_##reg); \
|
2006-01-19 06:19:26 +08:00
|
|
|
mutex_unlock(&data->update_lock); \
|
2005-04-26 04:43:25 +08:00
|
|
|
return count; \
|
|
|
|
}
|
|
|
|
store_temp1_reg(OVER, max);
|
|
|
|
store_temp1_reg(HYST, max_hyst);
|
|
|
|
|
|
|
|
#define show_temp_reg(reg) \
|
|
|
|
static ssize_t \
|
2006-02-06 06:24:16 +08:00
|
|
|
show_##reg(struct device *dev, struct device_attribute *attr, \
|
|
|
|
char *buf) \
|
2005-04-26 04:43:25 +08:00
|
|
|
{ \
|
|
|
|
struct w83627ehf_data *data = w83627ehf_update_device(dev); \
|
2006-02-06 06:24:16 +08:00
|
|
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
|
|
|
|
int nr = sensor_attr->index; \
|
2005-04-26 04:43:25 +08:00
|
|
|
return sprintf(buf, "%d\n", \
|
|
|
|
LM75_TEMP_FROM_REG(data->reg[nr])); \
|
|
|
|
}
|
|
|
|
show_temp_reg(temp);
|
|
|
|
show_temp_reg(temp_max);
|
|
|
|
show_temp_reg(temp_max_hyst);
|
|
|
|
|
|
|
|
#define store_temp_reg(REG, reg) \
|
|
|
|
static ssize_t \
|
2006-02-06 06:24:16 +08:00
|
|
|
store_##reg(struct device *dev, struct device_attribute *attr, \
|
|
|
|
const char *buf, size_t count) \
|
2005-04-26 04:43:25 +08:00
|
|
|
{ \
|
|
|
|
struct i2c_client *client = to_i2c_client(dev); \
|
|
|
|
struct w83627ehf_data *data = i2c_get_clientdata(client); \
|
2006-02-06 06:24:16 +08:00
|
|
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
|
|
|
|
int nr = sensor_attr->index; \
|
2005-04-26 04:43:25 +08:00
|
|
|
u32 val = simple_strtoul(buf, NULL, 10); \
|
|
|
|
\
|
2006-01-19 06:19:26 +08:00
|
|
|
mutex_lock(&data->update_lock); \
|
2005-04-26 04:43:25 +08:00
|
|
|
data->reg[nr] = LM75_TEMP_TO_REG(val); \
|
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_TEMP_##REG[nr], \
|
|
|
|
data->reg[nr]); \
|
2006-01-19 06:19:26 +08:00
|
|
|
mutex_unlock(&data->update_lock); \
|
2005-04-26 04:43:25 +08:00
|
|
|
return count; \
|
|
|
|
}
|
|
|
|
store_temp_reg(OVER, temp_max);
|
|
|
|
store_temp_reg(HYST, temp_max_hyst);
|
|
|
|
|
2006-02-06 06:24:16 +08:00
|
|
|
static struct sensor_device_attribute sda_temp[] = {
|
|
|
|
SENSOR_ATTR(temp1_input, S_IRUGO, show_temp1, NULL, 0),
|
|
|
|
SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 0),
|
|
|
|
SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 1),
|
|
|
|
SENSOR_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp1_max,
|
|
|
|
store_temp1_max, 0),
|
|
|
|
SENSOR_ATTR(temp2_max, S_IRUGO | S_IWUSR, show_temp_max,
|
|
|
|
store_temp_max, 0),
|
|
|
|
SENSOR_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max,
|
|
|
|
store_temp_max, 1),
|
|
|
|
SENSOR_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp1_max_hyst,
|
|
|
|
store_temp1_max_hyst, 0),
|
|
|
|
SENSOR_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst,
|
|
|
|
store_temp_max_hyst, 0),
|
|
|
|
SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst,
|
|
|
|
store_temp_max_hyst, 1),
|
2006-03-23 23:30:29 +08:00
|
|
|
SENSOR_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4),
|
|
|
|
SENSOR_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5),
|
|
|
|
SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13),
|
2006-02-06 06:24:16 +08:00
|
|
|
};
|
2005-04-26 04:43:25 +08:00
|
|
|
|
2006-07-06 00:14:31 +08:00
|
|
|
#define show_pwm_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, "%d\n", data->reg[nr]); \
|
|
|
|
}
|
|
|
|
|
|
|
|
show_pwm_reg(pwm_mode)
|
|
|
|
show_pwm_reg(pwm_enable)
|
|
|
|
show_pwm_reg(pwm)
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
store_pwm_mode(struct device *dev, struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count)
|
|
|
|
{
|
|
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
|
|
struct w83627ehf_data *data = i2c_get_clientdata(client);
|
|
|
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
|
|
|
int nr = sensor_attr->index;
|
|
|
|
u32 val = simple_strtoul(buf, NULL, 10);
|
|
|
|
u16 reg;
|
|
|
|
|
|
|
|
if (val > 1)
|
|
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&data->update_lock);
|
|
|
|
reg = w83627ehf_read_value(client, W83627EHF_REG_PWM_ENABLE[nr]);
|
|
|
|
data->pwm_mode[nr] = val;
|
|
|
|
reg &= ~(1 << W83627EHF_PWM_MODE_SHIFT[nr]);
|
|
|
|
if (!val)
|
|
|
|
reg |= 1 << W83627EHF_PWM_MODE_SHIFT[nr];
|
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_PWM_ENABLE[nr], reg);
|
|
|
|
mutex_unlock(&data->update_lock);
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
store_pwm(struct device *dev, struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count)
|
|
|
|
{
|
|
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
|
|
struct w83627ehf_data *data = i2c_get_clientdata(client);
|
|
|
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
|
|
|
int nr = sensor_attr->index;
|
|
|
|
u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 255);
|
|
|
|
|
|
|
|
mutex_lock(&data->update_lock);
|
|
|
|
data->pwm[nr] = val;
|
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_PWM[nr], val);
|
|
|
|
mutex_unlock(&data->update_lock);
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
store_pwm_enable(struct device *dev, struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count)
|
|
|
|
{
|
|
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
|
|
struct w83627ehf_data *data = i2c_get_clientdata(client);
|
|
|
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
|
|
|
int nr = sensor_attr->index;
|
|
|
|
u32 val = simple_strtoul(buf, NULL, 10);
|
|
|
|
u16 reg;
|
|
|
|
|
|
|
|
if (!val || (val > 2)) /* only modes 1 and 2 are supported */
|
|
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&data->update_lock);
|
|
|
|
reg = w83627ehf_read_value(client, W83627EHF_REG_PWM_ENABLE[nr]);
|
|
|
|
data->pwm_enable[nr] = val;
|
|
|
|
reg &= ~(0x03 << W83627EHF_PWM_ENABLE_SHIFT[nr]);
|
|
|
|
reg |= (val - 1) << W83627EHF_PWM_ENABLE_SHIFT[nr];
|
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_PWM_ENABLE[nr], reg);
|
|
|
|
mutex_unlock(&data->update_lock);
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define show_tol_temp(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, "%d\n", temp1_from_reg(data->reg[nr])); \
|
|
|
|
}
|
|
|
|
|
|
|
|
show_tol_temp(tolerance)
|
|
|
|
show_tol_temp(target_temp)
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
store_target_temp(struct device *dev, struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count)
|
|
|
|
{
|
|
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
|
|
struct w83627ehf_data *data = i2c_get_clientdata(client);
|
|
|
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
|
|
|
int nr = sensor_attr->index;
|
|
|
|
u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 127000);
|
|
|
|
|
|
|
|
mutex_lock(&data->update_lock);
|
|
|
|
data->target_temp[nr] = val;
|
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_TARGET[nr], val);
|
|
|
|
mutex_unlock(&data->update_lock);
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
store_tolerance(struct device *dev, struct device_attribute *attr,
|
|
|
|
const char *buf, size_t count)
|
|
|
|
{
|
|
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
|
|
struct w83627ehf_data *data = i2c_get_clientdata(client);
|
|
|
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
|
|
|
int nr = sensor_attr->index;
|
|
|
|
u16 reg;
|
|
|
|
/* Limit the temp to 0C - 15C */
|
|
|
|
u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 15000);
|
|
|
|
|
|
|
|
mutex_lock(&data->update_lock);
|
|
|
|
reg = w83627ehf_read_value(client, W83627EHF_REG_TOLERANCE[nr]);
|
|
|
|
data->tolerance[nr] = val;
|
|
|
|
if (nr == 1)
|
|
|
|
reg = (reg & 0x0f) | (val << 4);
|
|
|
|
else
|
|
|
|
reg = (reg & 0xf0) | val;
|
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_TOLERANCE[nr], reg);
|
|
|
|
mutex_unlock(&data->update_lock);
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct sensor_device_attribute sda_pwm[] = {
|
|
|
|
SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0),
|
|
|
|
SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1),
|
|
|
|
SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2),
|
|
|
|
SENSOR_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3),
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct sensor_device_attribute sda_pwm_mode[] = {
|
|
|
|
SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
|
|
|
|
store_pwm_mode, 0),
|
|
|
|
SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
|
|
|
|
store_pwm_mode, 1),
|
|
|
|
SENSOR_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
|
|
|
|
store_pwm_mode, 2),
|
|
|
|
SENSOR_ATTR(pwm4_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
|
|
|
|
store_pwm_mode, 3),
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct sensor_device_attribute sda_pwm_enable[] = {
|
|
|
|
SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
|
|
|
|
store_pwm_enable, 0),
|
|
|
|
SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
|
|
|
|
store_pwm_enable, 1),
|
|
|
|
SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
|
|
|
|
store_pwm_enable, 2),
|
|
|
|
SENSOR_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
|
|
|
|
store_pwm_enable, 3),
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct sensor_device_attribute sda_target_temp[] = {
|
|
|
|
SENSOR_ATTR(pwm1_target, S_IWUSR | S_IRUGO, show_target_temp,
|
|
|
|
store_target_temp, 0),
|
|
|
|
SENSOR_ATTR(pwm2_target, S_IWUSR | S_IRUGO, show_target_temp,
|
|
|
|
store_target_temp, 1),
|
|
|
|
SENSOR_ATTR(pwm3_target, S_IWUSR | S_IRUGO, show_target_temp,
|
|
|
|
store_target_temp, 2),
|
|
|
|
SENSOR_ATTR(pwm4_target, S_IWUSR | S_IRUGO, show_target_temp,
|
|
|
|
store_target_temp, 3),
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct sensor_device_attribute sda_tolerance[] = {
|
|
|
|
SENSOR_ATTR(pwm1_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
|
|
|
|
store_tolerance, 0),
|
|
|
|
SENSOR_ATTR(pwm2_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
|
|
|
|
store_tolerance, 1),
|
|
|
|
SENSOR_ATTR(pwm3_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
|
|
|
|
store_tolerance, 2),
|
|
|
|
SENSOR_ATTR(pwm4_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
|
|
|
|
store_tolerance, 3),
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Smart Fan registers */
|
|
|
|
|
|
|
|
#define fan_functions(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, "%d\n", data->reg[nr]); \
|
|
|
|
}\
|
|
|
|
static ssize_t \
|
|
|
|
store_##reg(struct device *dev, struct device_attribute *attr, \
|
|
|
|
const char *buf, size_t count) \
|
|
|
|
{\
|
|
|
|
struct i2c_client *client = to_i2c_client(dev); \
|
|
|
|
struct w83627ehf_data *data = i2c_get_clientdata(client); \
|
|
|
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
|
|
|
|
int nr = sensor_attr->index; \
|
|
|
|
u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 1, 255); \
|
|
|
|
mutex_lock(&data->update_lock); \
|
|
|
|
data->reg[nr] = val; \
|
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_##REG[nr], val); \
|
|
|
|
mutex_unlock(&data->update_lock); \
|
|
|
|
return count; \
|
|
|
|
}
|
|
|
|
|
|
|
|
fan_functions(fan_min_output, FAN_MIN_OUTPUT)
|
|
|
|
|
|
|
|
#define fan_time_functions(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, "%d\n", \
|
|
|
|
step_time_from_reg(data->reg[nr], data->pwm_mode[nr])); \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
static ssize_t \
|
|
|
|
store_##reg(struct device *dev, struct device_attribute *attr, \
|
|
|
|
const char *buf, size_t count) \
|
|
|
|
{ \
|
|
|
|
struct i2c_client *client = to_i2c_client(dev); \
|
|
|
|
struct w83627ehf_data *data = i2c_get_clientdata(client); \
|
|
|
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
|
|
|
|
int nr = sensor_attr->index; \
|
|
|
|
u8 val = step_time_to_reg(simple_strtoul(buf, NULL, 10), \
|
|
|
|
data->pwm_mode[nr]); \
|
|
|
|
mutex_lock(&data->update_lock); \
|
|
|
|
data->reg[nr] = val; \
|
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_##REG[nr], val); \
|
|
|
|
mutex_unlock(&data->update_lock); \
|
|
|
|
return count; \
|
|
|
|
} \
|
|
|
|
|
|
|
|
fan_time_functions(fan_stop_time, FAN_STOP_TIME)
|
|
|
|
|
|
|
|
|
|
|
|
static struct sensor_device_attribute sda_sf3_arrays_fan4[] = {
|
|
|
|
SENSOR_ATTR(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
|
|
|
|
store_fan_stop_time, 3),
|
|
|
|
SENSOR_ATTR(pwm4_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
|
|
|
|
store_fan_min_output, 3),
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct sensor_device_attribute sda_sf3_arrays[] = {
|
|
|
|
SENSOR_ATTR(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
|
|
|
|
store_fan_stop_time, 0),
|
|
|
|
SENSOR_ATTR(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
|
|
|
|
store_fan_stop_time, 1),
|
|
|
|
SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
|
|
|
|
store_fan_stop_time, 2),
|
|
|
|
SENSOR_ATTR(pwm1_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
|
|
|
|
store_fan_min_output, 0),
|
|
|
|
SENSOR_ATTR(pwm2_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
|
|
|
|
store_fan_min_output, 1),
|
|
|
|
SENSOR_ATTR(pwm3_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
|
|
|
|
store_fan_min_output, 2),
|
|
|
|
};
|
|
|
|
|
2005-04-26 04:43:25 +08:00
|
|
|
/*
|
|
|
|
* Driver and client management
|
|
|
|
*/
|
|
|
|
|
2006-09-25 03:04:38 +08:00
|
|
|
static void w83627ehf_device_remove_files(struct device *dev)
|
|
|
|
{
|
|
|
|
/* some entries in the following arrays may not have been used in
|
|
|
|
* device_create_file(), but device_remove_file() will ignore them */
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++)
|
|
|
|
device_remove_file(dev, &sda_sf3_arrays[i].dev_attr);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
|
|
|
|
device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
|
|
device_remove_file(dev, &sda_in_input[i].dev_attr);
|
|
|
|
device_remove_file(dev, &sda_in_alarm[i].dev_attr);
|
|
|
|
device_remove_file(dev, &sda_in_min[i].dev_attr);
|
|
|
|
device_remove_file(dev, &sda_in_max[i].dev_attr);
|
|
|
|
}
|
|
|
|
for (i = 0; i < 5; i++) {
|
|
|
|
device_remove_file(dev, &sda_fan_input[i].dev_attr);
|
|
|
|
device_remove_file(dev, &sda_fan_alarm[i].dev_attr);
|
|
|
|
device_remove_file(dev, &sda_fan_div[i].dev_attr);
|
|
|
|
device_remove_file(dev, &sda_fan_min[i].dev_attr);
|
|
|
|
}
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
device_remove_file(dev, &sda_pwm[i].dev_attr);
|
|
|
|
device_remove_file(dev, &sda_pwm_mode[i].dev_attr);
|
|
|
|
device_remove_file(dev, &sda_pwm_enable[i].dev_attr);
|
|
|
|
device_remove_file(dev, &sda_target_temp[i].dev_attr);
|
|
|
|
device_remove_file(dev, &sda_tolerance[i].dev_attr);
|
|
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
|
|
|
|
device_remove_file(dev, &sda_temp[i].dev_attr);
|
|
|
|
}
|
|
|
|
|
2005-04-26 04:43:25 +08:00
|
|
|
static struct i2c_driver w83627ehf_driver;
|
|
|
|
|
|
|
|
static void w83627ehf_init_client(struct i2c_client *client)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
u8 tmp;
|
|
|
|
|
|
|
|
/* Start monitoring is needed */
|
|
|
|
tmp = w83627ehf_read_value(client, W83627EHF_REG_CONFIG);
|
|
|
|
if (!(tmp & 0x01))
|
|
|
|
w83627ehf_write_value(client, W83627EHF_REG_CONFIG,
|
|
|
|
tmp | 0x01);
|
|
|
|
|
|
|
|
/* Enable temp2 and temp3 if needed */
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
tmp = w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_TEMP_CONFIG[i]);
|
|
|
|
if (tmp & 0x01)
|
|
|
|
w83627ehf_write_value(client,
|
|
|
|
W83627EHF_REG_TEMP_CONFIG[i],
|
|
|
|
tmp & 0xfe);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[PATCH] I2C: Separate non-i2c hwmon drivers from i2c-core (5/9)
Call the ISA chip drivers detection function directly instead of relying
on i2c_detect. The net effect is that address lists won't be handled
anymore, but they were mostly useless in the ISA case anyway (pc87360,
smsc47m1, smsc47b397 had already dropped them).
We don't need to handle multiple devices, all we may need is a way to
force a given address instead of the original one (some drivers already
do: sis5595, via686a, w83627hf), and, for drivers supporting multiple
chips, a way to force one given kind. All this may be added later on
demand, but I actually don't think there will be much demand.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-07-20 05:56:35 +08:00
|
|
|
static int w83627ehf_detect(struct i2c_adapter *adapter)
|
2005-04-26 04:43:25 +08:00
|
|
|
{
|
|
|
|
struct i2c_client *client;
|
|
|
|
struct w83627ehf_data *data;
|
2006-02-06 06:24:16 +08:00
|
|
|
struct device *dev;
|
2006-07-06 00:14:31 +08:00
|
|
|
u8 fan4pin, fan5pin;
|
2005-04-26 04:43:25 +08:00
|
|
|
int i, err = 0;
|
|
|
|
|
2005-10-08 05:11:03 +08:00
|
|
|
if (!request_region(address + REGION_OFFSET, REGION_LENGTH,
|
2005-11-27 03:37:41 +08:00
|
|
|
w83627ehf_driver.driver.name)) {
|
2005-04-26 04:43:25 +08:00
|
|
|
err = -EBUSY;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2005-10-18 05:08:32 +08:00
|
|
|
if (!(data = kzalloc(sizeof(struct w83627ehf_data), GFP_KERNEL))) {
|
2005-04-26 04:43:25 +08:00
|
|
|
err = -ENOMEM;
|
|
|
|
goto exit_release;
|
|
|
|
}
|
|
|
|
|
|
|
|
client = &data->client;
|
|
|
|
i2c_set_clientdata(client, data);
|
|
|
|
client->addr = address;
|
2006-01-19 06:19:26 +08:00
|
|
|
mutex_init(&data->lock);
|
2005-04-26 04:43:25 +08:00
|
|
|
client->adapter = adapter;
|
|
|
|
client->driver = &w83627ehf_driver;
|
|
|
|
client->flags = 0;
|
2006-02-06 06:24:16 +08:00
|
|
|
dev = &client->dev;
|
2005-04-26 04:43:25 +08:00
|
|
|
|
|
|
|
strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE);
|
|
|
|
data->valid = 0;
|
2006-01-19 06:19:26 +08:00
|
|
|
mutex_init(&data->update_lock);
|
2005-04-26 04:43:25 +08:00
|
|
|
|
|
|
|
/* Tell the i2c layer a new client has arrived */
|
|
|
|
if ((err = i2c_attach_client(client)))
|
|
|
|
goto exit_free;
|
|
|
|
|
|
|
|
/* Initialize the chip */
|
|
|
|
w83627ehf_init_client(client);
|
|
|
|
|
|
|
|
/* A few vars need to be filled upon startup */
|
|
|
|
for (i = 0; i < 5; i++)
|
|
|
|
data->fan_min[i] = w83627ehf_read_value(client,
|
|
|
|
W83627EHF_REG_FAN_MIN[i]);
|
|
|
|
|
2006-07-06 00:14:31 +08:00
|
|
|
/* fan4 and fan5 share some pins with the GPIO and serial flash */
|
|
|
|
|
|
|
|
superio_enter();
|
|
|
|
fan5pin = superio_inb(0x24) & 0x2;
|
|
|
|
fan4pin = superio_inb(0x29) & 0x6;
|
|
|
|
superio_exit();
|
|
|
|
|
2005-04-26 04:43:25 +08:00
|
|
|
/* It looks like fan4 and fan5 pins can be alternatively used
|
2006-10-09 04:02:09 +08:00
|
|
|
as fan on/off switches, but fan5 control is write only :/
|
|
|
|
We assume that if the serial interface is disabled, designers
|
|
|
|
connected fan5 as input unless they are emitting log 1, which
|
|
|
|
is not the default. */
|
2006-07-06 00:14:31 +08:00
|
|
|
|
2005-04-26 04:43:25 +08:00
|
|
|
data->has_fan = 0x07; /* fan1, fan2 and fan3 */
|
|
|
|
i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1);
|
2006-07-06 00:14:31 +08:00
|
|
|
if ((i & (1 << 2)) && (!fan4pin))
|
2005-04-26 04:43:25 +08:00
|
|
|
data->has_fan |= (1 << 3);
|
2006-10-09 04:02:09 +08:00
|
|
|
if (!(i & (1 << 1)) && (!fan5pin))
|
2005-04-26 04:43:25 +08:00
|
|
|
data->has_fan |= (1 << 4);
|
|
|
|
|
|
|
|
/* Register sysfs hooks */
|
2006-07-06 00:14:31 +08:00
|
|
|
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++)
|
2006-09-25 03:04:38 +08:00
|
|
|
if ((err = device_create_file(dev,
|
|
|
|
&sda_sf3_arrays[i].dev_attr)))
|
|
|
|
goto exit_remove;
|
2006-07-06 00:14:31 +08:00
|
|
|
|
|
|
|
/* if fan4 is enabled create the sf3 files for it */
|
|
|
|
if (data->has_fan & (1 << 3))
|
2006-09-25 03:04:38 +08:00
|
|
|
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) {
|
|
|
|
if ((err = device_create_file(dev,
|
|
|
|
&sda_sf3_arrays_fan4[i].dev_attr)))
|
|
|
|
goto exit_remove;
|
|
|
|
}
|
2006-07-06 00:14:31 +08:00
|
|
|
|
2006-03-23 23:25:22 +08:00
|
|
|
for (i = 0; i < 10; i++)
|
2006-09-25 03:04:38 +08:00
|
|
|
if ((err = device_create_file(dev, &sda_in_input[i].dev_attr))
|
|
|
|
|| (err = device_create_file(dev,
|
|
|
|
&sda_in_alarm[i].dev_attr))
|
|
|
|
|| (err = device_create_file(dev,
|
|
|
|
&sda_in_min[i].dev_attr))
|
|
|
|
|| (err = device_create_file(dev,
|
|
|
|
&sda_in_max[i].dev_attr)))
|
|
|
|
goto exit_remove;
|
2006-03-23 23:25:22 +08:00
|
|
|
|
2006-02-06 06:24:16 +08:00
|
|
|
for (i = 0; i < 5; i++) {
|
2006-07-06 00:14:31 +08:00
|
|
|
if (data->has_fan & (1 << i)) {
|
2006-09-25 03:04:38 +08:00
|
|
|
if ((err = device_create_file(dev,
|
|
|
|
&sda_fan_input[i].dev_attr))
|
|
|
|
|| (err = device_create_file(dev,
|
|
|
|
&sda_fan_alarm[i].dev_attr))
|
|
|
|
|| (err = device_create_file(dev,
|
|
|
|
&sda_fan_div[i].dev_attr))
|
|
|
|
|| (err = device_create_file(dev,
|
|
|
|
&sda_fan_min[i].dev_attr)))
|
|
|
|
goto exit_remove;
|
|
|
|
if (i < 4 && /* w83627ehf only has 4 pwm */
|
|
|
|
((err = device_create_file(dev,
|
|
|
|
&sda_pwm[i].dev_attr))
|
|
|
|
|| (err = device_create_file(dev,
|
|
|
|
&sda_pwm_mode[i].dev_attr))
|
|
|
|
|| (err = device_create_file(dev,
|
|
|
|
&sda_pwm_enable[i].dev_attr))
|
|
|
|
|| (err = device_create_file(dev,
|
|
|
|
&sda_target_temp[i].dev_attr))
|
|
|
|
|| (err = device_create_file(dev,
|
|
|
|
&sda_tolerance[i].dev_attr))))
|
|
|
|
goto exit_remove;
|
2006-07-06 00:14:31 +08:00
|
|
|
}
|
2005-04-26 04:43:25 +08:00
|
|
|
}
|
2006-07-06 00:14:31 +08:00
|
|
|
|
2006-02-06 06:24:16 +08:00
|
|
|
for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
|
2006-09-25 03:04:38 +08:00
|
|
|
if ((err = device_create_file(dev, &sda_temp[i].dev_attr)))
|
|
|
|
goto exit_remove;
|
|
|
|
|
|
|
|
data->class_dev = hwmon_device_register(dev);
|
|
|
|
if (IS_ERR(data->class_dev)) {
|
|
|
|
err = PTR_ERR(data->class_dev);
|
|
|
|
goto exit_remove;
|
|
|
|
}
|
2005-04-26 04:43:25 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2006-09-25 03:04:38 +08:00
|
|
|
exit_remove:
|
|
|
|
w83627ehf_device_remove_files(dev);
|
2005-07-16 09:39:18 +08:00
|
|
|
i2c_detach_client(client);
|
2005-04-26 04:43:25 +08:00
|
|
|
exit_free:
|
|
|
|
kfree(data);
|
|
|
|
exit_release:
|
2005-10-08 05:11:03 +08:00
|
|
|
release_region(address + REGION_OFFSET, REGION_LENGTH);
|
2005-04-26 04:43:25 +08:00
|
|
|
exit:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int w83627ehf_detach_client(struct i2c_client *client)
|
|
|
|
{
|
2005-07-16 09:39:18 +08:00
|
|
|
struct w83627ehf_data *data = i2c_get_clientdata(client);
|
2005-04-26 04:43:25 +08:00
|
|
|
int err;
|
|
|
|
|
2005-07-16 09:39:18 +08:00
|
|
|
hwmon_device_unregister(data->class_dev);
|
2006-09-25 03:04:38 +08:00
|
|
|
w83627ehf_device_remove_files(&client->dev);
|
2005-07-16 09:39:18 +08:00
|
|
|
|
2005-07-28 04:14:49 +08:00
|
|
|
if ((err = i2c_detach_client(client)))
|
2005-04-26 04:43:25 +08:00
|
|
|
return err;
|
2005-10-08 05:11:03 +08:00
|
|
|
release_region(client->addr + REGION_OFFSET, REGION_LENGTH);
|
2005-07-16 09:39:18 +08:00
|
|
|
kfree(data);
|
2005-04-26 04:43:25 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct i2c_driver w83627ehf_driver = {
|
2005-11-27 03:37:41 +08:00
|
|
|
.driver = {
|
2006-09-04 04:36:14 +08:00
|
|
|
.owner = THIS_MODULE,
|
2005-11-27 03:37:41 +08:00
|
|
|
.name = "w83627ehf",
|
|
|
|
},
|
[PATCH] I2C: Separate non-i2c hwmon drivers from i2c-core (5/9)
Call the ISA chip drivers detection function directly instead of relying
on i2c_detect. The net effect is that address lists won't be handled
anymore, but they were mostly useless in the ISA case anyway (pc87360,
smsc47m1, smsc47b397 had already dropped them).
We don't need to handle multiple devices, all we may need is a way to
force a given address instead of the original one (some drivers already
do: sis5595, via686a, w83627hf), and, for drivers supporting multiple
chips, a way to force one given kind. All this may be added later on
demand, but I actually don't think there will be much demand.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-07-20 05:56:35 +08:00
|
|
|
.attach_adapter = w83627ehf_detect,
|
2005-04-26 04:43:25 +08:00
|
|
|
.detach_client = w83627ehf_detach_client,
|
|
|
|
};
|
|
|
|
|
[PATCH] I2C: Separate non-i2c hwmon drivers from i2c-core (5/9)
Call the ISA chip drivers detection function directly instead of relying
on i2c_detect. The net effect is that address lists won't be handled
anymore, but they were mostly useless in the ISA case anyway (pc87360,
smsc47m1, smsc47b397 had already dropped them).
We don't need to handle multiple devices, all we may need is a way to
force a given address instead of the original one (some drivers already
do: sis5595, via686a, w83627hf), and, for drivers supporting multiple
chips, a way to force one given kind. All this may be added later on
demand, but I actually don't think there will be much demand.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-07-20 05:56:35 +08:00
|
|
|
static int __init w83627ehf_find(int sioaddr, unsigned short *addr)
|
2005-04-26 04:43:25 +08:00
|
|
|
{
|
|
|
|
u16 val;
|
|
|
|
|
|
|
|
REG = sioaddr;
|
|
|
|
VAL = sioaddr + 1;
|
|
|
|
superio_enter();
|
|
|
|
|
|
|
|
val = (superio_inb(SIO_REG_DEVID) << 8)
|
|
|
|
| superio_inb(SIO_REG_DEVID + 1);
|
|
|
|
if ((val & SIO_ID_MASK) != SIO_W83627EHF_ID) {
|
|
|
|
superio_exit();
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
superio_select(W83627EHF_LD_HWM);
|
|
|
|
val = (superio_inb(SIO_REG_ADDR) << 8)
|
|
|
|
| superio_inb(SIO_REG_ADDR + 1);
|
2005-10-08 05:11:03 +08:00
|
|
|
*addr = val & REGION_ALIGNMENT;
|
[PATCH] I2C: Separate non-i2c hwmon drivers from i2c-core (5/9)
Call the ISA chip drivers detection function directly instead of relying
on i2c_detect. The net effect is that address lists won't be handled
anymore, but they were mostly useless in the ISA case anyway (pc87360,
smsc47m1, smsc47b397 had already dropped them).
We don't need to handle multiple devices, all we may need is a way to
force a given address instead of the original one (some drivers already
do: sis5595, via686a, w83627hf), and, for drivers supporting multiple
chips, a way to force one given kind. All this may be added later on
demand, but I actually don't think there will be much demand.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-07-20 05:56:35 +08:00
|
|
|
if (*addr == 0) {
|
2005-04-26 04:43:25 +08:00
|
|
|
superio_exit();
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Activate logical device if needed */
|
|
|
|
val = superio_inb(SIO_REG_ENABLE);
|
|
|
|
if (!(val & 0x01))
|
|
|
|
superio_outb(SIO_REG_ENABLE, val | 0x01);
|
|
|
|
|
|
|
|
superio_exit();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __init sensors_w83627ehf_init(void)
|
|
|
|
{
|
[PATCH] I2C: Separate non-i2c hwmon drivers from i2c-core (5/9)
Call the ISA chip drivers detection function directly instead of relying
on i2c_detect. The net effect is that address lists won't be handled
anymore, but they were mostly useless in the ISA case anyway (pc87360,
smsc47m1, smsc47b397 had already dropped them).
We don't need to handle multiple devices, all we may need is a way to
force a given address instead of the original one (some drivers already
do: sis5595, via686a, w83627hf), and, for drivers supporting multiple
chips, a way to force one given kind. All this may be added later on
demand, but I actually don't think there will be much demand.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-07-20 05:56:35 +08:00
|
|
|
if (w83627ehf_find(0x2e, &address)
|
|
|
|
&& w83627ehf_find(0x4e, &address))
|
2005-04-26 04:43:25 +08:00
|
|
|
return -ENODEV;
|
|
|
|
|
[PATCH] I2C: Separate non-i2c hwmon drivers from i2c-core (3/9)
Convert the 10 ISA hardware monitoring drivers (it87, lm78, pc87360,
sis5595, smsc47b397, smsc47m1, via686a, w83627hf, w83627ehf, w83781d) to
explicitely register with i2c-isa. For hybrid drivers (it87, lm78,
w83781d), we now have two separate instances of i2c_driver, one for the
I2C interface of the chip, and one for ISA interface. In the long run,
the one for ISA will be replaced with a different driver type.
At this point, all drivers are working again, except for missing
dependencies in Kconfig.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-07-20 05:51:07 +08:00
|
|
|
return i2c_isa_add_driver(&w83627ehf_driver);
|
2005-04-26 04:43:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit sensors_w83627ehf_exit(void)
|
|
|
|
{
|
[PATCH] I2C: Separate non-i2c hwmon drivers from i2c-core (3/9)
Convert the 10 ISA hardware monitoring drivers (it87, lm78, pc87360,
sis5595, smsc47b397, smsc47m1, via686a, w83627hf, w83627ehf, w83781d) to
explicitely register with i2c-isa. For hybrid drivers (it87, lm78,
w83781d), we now have two separate instances of i2c_driver, one for the
I2C interface of the chip, and one for ISA interface. In the long run,
the one for ISA will be replaced with a different driver type.
At this point, all drivers are working again, except for missing
dependencies in Kconfig.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-07-20 05:51:07 +08:00
|
|
|
i2c_isa_del_driver(&w83627ehf_driver);
|
2005-04-26 04:43:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
|
|
|
|
MODULE_DESCRIPTION("W83627EHF driver");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
|
|
module_init(sensors_w83627ehf_init);
|
|
|
|
module_exit(sensors_w83627ehf_exit);
|