w83l786ng.c revision 85f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671
185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/*
285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo    w83l786ng.c - Linux kernel driver for hardware monitoring
385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo    Copyright (c) 2007 Kevin Lo <kevlo@kevlo.org>
485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo    This program is free software; you can redistribute it and/or modify
685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo    it under the terms of the GNU General Public License as published by
785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo    the Free Software Foundation - version 2.
885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo    This program is distributed in the hope that it will be useful,
1085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo    but WITHOUT ANY WARRANTY; without even the implied warranty of
1185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo    GNU General Public License for more details.
1385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
1485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo    You should have received a copy of the GNU General Public License
1585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo    along with this program; if not, write to the Free Software
1685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
1785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo    02110-1301 USA.
1885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo*/
1985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
2085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/*
2185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo    Supports following chips:
2285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
2385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo    Chip	#vin	#fanin	#pwm	#temp	wchipid	vendid	i2c	ISA
2485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo    w83l786ng	3	2	2	2	0x7b	0x5ca3	yes	no
2585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo*/
2685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
2785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#include <linux/module.h>
2885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#include <linux/init.h>
2985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#include <linux/slab.h>
3085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#include <linux/i2c.h>
3185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#include <linux/hwmon.h>
3285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#include <linux/hwmon-vid.h>
3385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#include <linux/hwmon-sysfs.h>
3485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#include <linux/err.h>
3585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#include <linux/mutex.h>
3685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
3785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/* Addresses to scan */
3885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic unsigned short normal_i2c[] = { 0x2e, 0x2f, I2C_CLIENT_END };
3985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
4085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/* Insmod parameters */
4185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin LoI2C_CLIENT_INSMOD_1(w83l786ng);
4285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
4385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic int reset;
4485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lomodule_param(reset, bool, 0);
4585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin LoMODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended");
4685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
4785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_IN_MIN(nr)	(0x2C + (nr) * 2)
4885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_IN_MAX(nr)	(0x2B + (nr) * 2)
4985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_IN(nr)		((nr) + 0x20)
5085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
5185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_FAN(nr)		((nr) + 0x28)
5285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_FAN_MIN(nr)	((nr) + 0x3B)
5385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
5485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_CONFIG		0x40
5585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_ALARM1		0x41
5685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_ALARM2 		0x42
5785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_GPIO_EN		0x47
5885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_MAN_ID2		0x4C
5985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_MAN_ID1		0x4D
6085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_CHIP_ID		0x4E
6185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
6285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_DIODE		0x53
6385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_FAN_DIV		0x54
6485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_FAN_CFG		0x80
6585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
6685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define W83L786NG_REG_TOLERANCE		0x8D
6785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
6885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic const u8 W83L786NG_REG_TEMP[2][3] = {
6985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	{ 0x25,		/* TEMP 0 in DataSheet */
7085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	  0x35,		/* TEMP 0 Over in DataSheet */
7185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	  0x36 },	/* TEMP 0 Hyst in DataSheet */
7285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	{ 0x26,		/* TEMP 1 in DataSheet */
7385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	  0x37,		/* TEMP 1 Over in DataSheet */
7485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	  0x38 }	/* TEMP 1 Hyst in DataSheet */
7585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo};
7685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
7785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic const u8 W83L786NG_PWM_MODE_SHIFT[] = {6, 7};
7885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic const u8 W83L786NG_PWM_ENABLE_SHIFT[] = {2, 4};
7985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
8085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/* FAN Duty Cycle, be used to control */
8185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic const u8 W83L786NG_REG_PWM[] = {0x81, 0x87};
8285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
8385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
8485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic inline u8
8585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin LoFAN_TO_REG(long rpm, int div)
8685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
8785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	if (rpm == 0)
8885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		return 255;
8985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	rpm = SENSORS_LIMIT(rpm, 1, 1000000);
9085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
9185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
9285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
9385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define FAN_FROM_REG(val,div)	((val) == 0   ? -1 : \
9485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo				((val) == 255 ? 0 : \
9585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo				1350000 / ((val) * (div))))
9685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
9785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/* for temp */
9885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define TEMP_TO_REG(val)	(SENSORS_LIMIT(((val) < 0 ? (val)+0x100*1000 \
9985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo				    : (val)) / 1000, 0, 0xff))
10085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define TEMP_FROM_REG(val)	(((val) & 0x80 ? (val)-0x100 : (val)) * 1000)
10185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
10285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/* The analog voltage inputs have 8mV LSB. Since the sysfs output is
10385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo   in mV as would be measured on the chip input pin, need to just
10485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo   multiply/divide by 8 to translate from/to register values. */
10585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define IN_TO_REG(val)          (SENSORS_LIMIT((((val) + 4) / 8), 0, 255))
10685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define IN_FROM_REG(val)	((val) * 8)
10785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
10885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define DIV_FROM_REG(val)	(1 << (val))
10985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
11085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic inline u8
11185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin LoDIV_TO_REG(long val)
11285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
11385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int i;
11485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	val = SENSORS_LIMIT(val, 1, 128) >> 1;
11585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	for (i = 0; i < 7; i++) {
11685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		if (val == 0)
11785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			break;
11885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		val >>= 1;
11985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	}
12085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return ((u8) i);
12185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
12285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
12385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostruct w83l786ng_data {
12485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct i2c_client client;
12585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct device *hwmon_dev;
12685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct mutex update_lock;
12785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	char valid;			/* !=0 if following fields are valid */
12885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	unsigned long last_updated;	/* In jiffies */
12985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	unsigned long last_nonvolatile;	/* In jiffies, last time we update the
13085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo					   nonvolatile registers */
13185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
13285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 in[3];
13385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 in_max[3];
13485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 in_min[3];
13585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 fan[2];
13685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 fan_div[2];
13785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 fan_min[2];
13885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 temp_type[2];
13985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 temp[2][3];
14085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 pwm[2];
14185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 pwm_mode[2];	/* 0->DC variable voltage
14285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			   1->PWM variable duty cycle */
14385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
14485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 pwm_enable[2]; /* 1->manual
14585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			     2->thermal cruise (also called SmartFan I) */
14685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 tolerance[2];
14785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo};
14885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
14985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic int w83l786ng_attach_adapter(struct i2c_adapter *adapter);
15085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic int w83l786ng_detect(struct i2c_adapter *adapter, int address, int kind);
15185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic int w83l786ng_detach_client(struct i2c_client *client);
15285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic void w83l786ng_init_client(struct i2c_client *client);
15385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct w83l786ng_data *w83l786ng_update_device(struct device *dev);
15485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
15585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct i2c_driver w83l786ng_driver = {
15685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	.driver = {
15785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		   .name = "w83l786ng",
15885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	},
15985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	.attach_adapter = w83l786ng_attach_adapter,
16085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	.detach_client = w83l786ng_detach_client,
16185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo};
16285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
16385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic u8
16485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Low83l786ng_read_value(struct i2c_client *client, u8 reg)
16585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
16685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return i2c_smbus_read_byte_data(client, reg);
16785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
16885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
16985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic int
17085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Low83l786ng_write_value(struct i2c_client *client, u8 reg, u8 value)
17185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
17285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return i2c_smbus_write_byte_data(client, reg, value);
17385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
17485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
17585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/* following are the sysfs callback functions */
17685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define show_in_reg(reg) \
17785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t \
17885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_##reg(struct device *dev, struct device_attribute *attr, \
17985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo           char *buf) \
18085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ \
18185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int nr = to_sensor_dev_attr(attr)->index; \
18285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct w83l786ng_data *data = w83l786ng_update_device(dev); \
18385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return sprintf(buf,"%d\n", IN_FROM_REG(data->reg[nr])); \
18485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
18585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
18685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_in_reg(in)
18785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_in_reg(in_min)
18885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_in_reg(in_max)
18985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
19085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define store_in_reg(REG, reg) \
19185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t \
19285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_in_##reg (struct device *dev, struct device_attribute *attr, \
19385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		const char *buf, size_t count) \
19485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ \
19585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int nr = to_sensor_dev_attr(attr)->index; \
19685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct i2c_client *client = to_i2c_client(dev); \
19785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct w83l786ng_data *data = i2c_get_clientdata(client); \
19885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	unsigned long val = simple_strtoul(buf, NULL, 10); \
19985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_lock(&data->update_lock); \
20085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	data->in_##reg[nr] = IN_TO_REG(val); \
20185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	w83l786ng_write_value(client, W83L786NG_REG_IN_##REG(nr), \
20285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			      data->in_##reg[nr]); \
20385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_unlock(&data->update_lock); \
20485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return count; \
20585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
20685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
20785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_in_reg(MIN, min)
20885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_in_reg(MAX, max)
20985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
21085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_in_input[] = {
21185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0),
21285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1),
21385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2),
21485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo};
21585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
21685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_in_min[] = {
21785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0),
21885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1),
21985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2),
22085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo};
22185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
22285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_in_max[] = {
22385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0),
22485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1),
22585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2),
22685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo};
22785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
22885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define show_fan_reg(reg) \
22985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
23085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			  char *buf) \
23185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ \
23285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int nr = to_sensor_dev_attr(attr)->index; \
23385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct w83l786ng_data *data = w83l786ng_update_device(dev); \
23485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo        return sprintf(buf,"%d\n", \
23585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo                FAN_FROM_REG(data->fan[nr], DIV_FROM_REG(data->fan_div[nr]))); \
23685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
23785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
23885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_fan_reg(fan);
23985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_fan_reg(fan_min);
24085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
24185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t
24285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_fan_min(struct device *dev, struct device_attribute *attr,
24385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	      const char *buf, size_t count)
24485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
24585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int nr = to_sensor_dev_attr(attr)->index;
24685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct i2c_client *client = to_i2c_client(dev);
24785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct w83l786ng_data *data = i2c_get_clientdata(client);
24885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u32 val;
24985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
25085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	val = simple_strtoul(buf, NULL, 10);
25185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_lock(&data->update_lock);
25285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
25385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	w83l786ng_write_value(client, W83L786NG_REG_FAN_MIN(nr),
25485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			      data->fan_min[nr]);
25585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_unlock(&data->update_lock);
25685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
25785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return count;
25885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
25985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
26085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t
26185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_fan_div(struct device *dev, struct device_attribute *attr,
26285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	     char *buf)
26385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
26485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int nr = to_sensor_dev_attr(attr)->index;
26585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct w83l786ng_data *data = w83l786ng_update_device(dev);
26685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return sprintf(buf, "%u\n", DIV_FROM_REG(data->fan_div[nr]));
26785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
26885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
26985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/* Note: we save and restore the fan minimum here, because its value is
27085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo   determined in part by the fan divisor.  This follows the principle of
27185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo   least surprise; the user doesn't expect the fan minimum to change just
27285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo   because the divisor changed. */
27385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t
27485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_fan_div(struct device *dev, struct device_attribute *attr,
27585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	      const char *buf, size_t count)
27685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
27785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int nr = to_sensor_dev_attr(attr)->index;
27885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct i2c_client *client = to_i2c_client(dev);
27985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct w83l786ng_data *data = i2c_get_clientdata(client);
28085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
28185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	unsigned long min;
28285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 tmp_fan_div;
28385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 fan_div_reg;
28485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 keep_mask = 0;
28585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 new_shift = 0;
28685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
28785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	/* Save fan_min */
28885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_lock(&data->update_lock);
28985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]));
29085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
29185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	data->fan_div[nr] = DIV_TO_REG(simple_strtoul(buf, NULL, 10));
29285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
29385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	switch (nr) {
29485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	case 0:
29585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		keep_mask = 0xf8;
29685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		new_shift = 0;
29785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		break;
29885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	case 1:
29985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		keep_mask = 0x8f;
30085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		new_shift = 4;
30185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		break;
30285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	}
30385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
30485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	fan_div_reg = w83l786ng_read_value(client, W83L786NG_REG_FAN_DIV)
30585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo					   & keep_mask;
30685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
30785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	tmp_fan_div = (data->fan_div[nr] << new_shift) & ~keep_mask;
30885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
30985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	w83l786ng_write_value(client, W83L786NG_REG_FAN_DIV,
31085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			      fan_div_reg | tmp_fan_div);
31185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
31285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	/* Restore fan_min */
31385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
31485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	w83l786ng_write_value(client, W83L786NG_REG_FAN_MIN(nr),
31585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			      data->fan_min[nr]);
31685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_unlock(&data->update_lock);
31785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
31885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return count;
31985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
32085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
32185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_fan_input[] = {
32285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0),
32385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1),
32485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo};
32585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
32685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_fan_min[] = {
32785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
32885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		    store_fan_min, 0),
32985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min,
33085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		    store_fan_min, 1),
33185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo};
33285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
33385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_fan_div[] = {
33485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(fan1_div, S_IWUSR | S_IRUGO, show_fan_div,
33585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		    store_fan_div, 0),
33685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(fan2_div, S_IWUSR | S_IRUGO, show_fan_div,
33785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		    store_fan_div, 1),
33885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo};
33985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
34085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
34185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/* read/write the temperature, includes measured value and limits */
34285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
34385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t
34485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_temp(struct device *dev, struct device_attribute *attr, char *buf)
34585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
34685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct sensor_device_attribute_2 *sensor_attr =
34785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	    to_sensor_dev_attr_2(attr);
34885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int nr = sensor_attr->nr;
34985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int index = sensor_attr->index;
35085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct w83l786ng_data *data = w83l786ng_update_device(dev);
35185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr][index]));
35285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
35385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
35485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t
35585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_temp(struct device *dev, struct device_attribute *attr,
35685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	   const char *buf, size_t count)
35785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
35885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct sensor_device_attribute_2 *sensor_attr =
35985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	    to_sensor_dev_attr_2(attr);
36085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int nr = sensor_attr->nr;
36185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int index = sensor_attr->index;
36285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct i2c_client *client = to_i2c_client(dev);
36385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct w83l786ng_data *data = i2c_get_clientdata(client);
36485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	s32 val;
36585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
36685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	val = simple_strtol(buf, NULL, 10);
36785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_lock(&data->update_lock);
36885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	data->temp[nr][index] = TEMP_TO_REG(val);
36985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	w83l786ng_write_value(client, W83L786NG_REG_TEMP[nr][index],
37085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			      data->temp[nr][index]);
37185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_unlock(&data->update_lock);
37285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
37385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo        return count;
37485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
37585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
37685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute_2 sda_temp_input[] = {
37785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0),
37885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 1, 0),
37985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo};
38085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
38185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute_2 sda_temp_max[] = {
38285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR_2(temp1_max, S_IRUGO | S_IWUSR,
38385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		      show_temp, store_temp, 0, 1),
38485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR_2(temp2_max, S_IRUGO | S_IWUSR,
38585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		      show_temp, store_temp, 1, 1),
38685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo};
38785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
38885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute_2 sda_temp_max_hyst[] = {
38985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR,
39085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		      show_temp, store_temp, 0, 2),
39185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR,
39285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		      show_temp, store_temp, 1, 2),
39385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo};
39485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
39585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define show_pwm_reg(reg) \
39685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t show_##reg (struct device *dev, struct device_attribute *attr, \
39785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			   char *buf) \
39885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{ \
39985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct w83l786ng_data *data = w83l786ng_update_device(dev); \
40085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int nr = to_sensor_dev_attr(attr)->index; \
40185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return sprintf(buf, "%d\n", data->reg[nr]); \
40285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
40385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
40485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_pwm_reg(pwm_mode)
40585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_pwm_reg(pwm_enable)
40685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_pwm_reg(pwm)
40785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
40885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t
40985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_pwm_mode(struct device *dev, struct device_attribute *attr,
41085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	       const char *buf, size_t count)
41185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
41285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int nr = to_sensor_dev_attr(attr)->index;
41385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct i2c_client *client = to_i2c_client(dev);
41485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct w83l786ng_data *data = i2c_get_clientdata(client);
41585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u32 val = simple_strtoul(buf, NULL, 10);
41685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 reg;
41785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
41885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	if (val > 1)
41985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		return -EINVAL;
42085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_lock(&data->update_lock);
42185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	data->pwm_mode[nr] = val;
42285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	reg = w83l786ng_read_value(client, W83L786NG_REG_FAN_CFG);
42385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	reg &= ~(1 << W83L786NG_PWM_MODE_SHIFT[nr]);
42485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	if (!val)
42585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		reg |= 1 << W83L786NG_PWM_MODE_SHIFT[nr];
42685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	w83l786ng_write_value(client, W83L786NG_REG_FAN_CFG, reg);
42785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_unlock(&data->update_lock);
42885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return count;
42985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
43085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
43185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t
43285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_pwm(struct device *dev, struct device_attribute *attr,
43385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	  const char *buf, size_t count)
43485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
43585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int nr = to_sensor_dev_attr(attr)->index;
43685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct i2c_client *client = to_i2c_client(dev);
43785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct w83l786ng_data *data = i2c_get_clientdata(client);
43885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 255);
43985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
44085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_lock(&data->update_lock);
44185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	data->pwm[nr] = val;
44285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	w83l786ng_write_value(client, W83L786NG_REG_PWM[nr], val);
44385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_unlock(&data->update_lock);
44485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return count;
44585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
44685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
44785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t
44885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_pwm_enable(struct device *dev, struct device_attribute *attr,
44985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		 const char *buf, size_t count)
45085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
45185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int nr = to_sensor_dev_attr(attr)->index;
45285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct i2c_client *client = to_i2c_client(dev);
45385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct w83l786ng_data *data = i2c_get_clientdata(client);
45485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u32 val = simple_strtoul(buf, NULL, 10);
45585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
45685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 reg;
45785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
45885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	if (!val || (val > 2))  /* only modes 1 and 2 are supported */
45985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		return -EINVAL;
46085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
46185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_lock(&data->update_lock);
46285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	reg = w83l786ng_read_value(client, W83L786NG_REG_FAN_CFG);
46385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	data->pwm_enable[nr] = val;
46485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	reg &= ~(0x02 << W83L786NG_PWM_ENABLE_SHIFT[nr]);
46585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	reg |= (val - 1) << W83L786NG_PWM_ENABLE_SHIFT[nr];
46685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	w83l786ng_write_value(client, W83L786NG_REG_FAN_CFG, reg);
46785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_unlock(&data->update_lock);
46885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return count;
46985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
47085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
47185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_pwm[] = {
47285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0),
47385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1),
47485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo};
47585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
47685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_pwm_mode[] = {
47785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
47885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		    store_pwm_mode, 0),
47985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
48085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		    store_pwm_mode, 1),
48185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo};
48285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
48385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_pwm_enable[] = {
48485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
48585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		    store_pwm_enable, 0),
48685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
48785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		    store_pwm_enable, 1),
48885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo};
48985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
49085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo/* For Smart Fan I/Thermal Cruise and Smart Fan II */
49185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t
49285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loshow_tolerance(struct device *dev, struct device_attribute *attr, char *buf)
49385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
49485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int nr = to_sensor_dev_attr(attr)->index;
49585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct w83l786ng_data *data = w83l786ng_update_device(dev);
49685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return sprintf(buf, "%ld\n", (long)data->tolerance[nr]);
49785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
49885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
49985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic ssize_t
50085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostore_tolerance(struct device *dev, struct device_attribute *attr,
50185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		const char *buf, size_t count)
50285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
50385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int nr = to_sensor_dev_attr(attr)->index;
50485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct i2c_client *client = to_i2c_client(dev);
50585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct w83l786ng_data *data = i2c_get_clientdata(client);
50685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u32 val;
50785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 tol_tmp, tol_mask;
50885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
50985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	val = simple_strtoul(buf, NULL, 10);
51085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
51185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_lock(&data->update_lock);
51285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	tol_mask = w83l786ng_read_value(client,
51385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	    W83L786NG_REG_TOLERANCE) & ((nr == 1) ? 0x0f : 0xf0);
51485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	tol_tmp = SENSORS_LIMIT(val, 0, 15);
51585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	tol_tmp &= 0x0f;
51685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	data->tolerance[nr] = tol_tmp;
51785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	if (nr == 1) {
51885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		tol_tmp <<= 4;
51985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	}
52085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
52185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	w83l786ng_write_value(client, W83L786NG_REG_TOLERANCE,
52285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			      tol_mask | tol_tmp);
52385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_unlock(&data->update_lock);
52485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return count;
52585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
52685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
52785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct sensor_device_attribute sda_tolerance[] = {
52885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(pwm1_tolerance, S_IWUSR | S_IRUGO,
52985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		    show_tolerance, store_tolerance, 0),
53085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	SENSOR_ATTR(pwm2_tolerance, S_IWUSR | S_IRUGO,
53185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		    show_tolerance, store_tolerance, 1),
53285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo};
53385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
53485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
53585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define IN_UNIT_ATTRS(X)	\
53685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	&sda_in_input[X].dev_attr.attr,		\
53785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	&sda_in_min[X].dev_attr.attr,		\
53885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	&sda_in_max[X].dev_attr.attr
53985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
54085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define FAN_UNIT_ATTRS(X)	\
54185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	&sda_fan_input[X].dev_attr.attr,	\
54285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	&sda_fan_min[X].dev_attr.attr,		\
54385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	&sda_fan_div[X].dev_attr.attr
54485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
54585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define TEMP_UNIT_ATTRS(X)	\
54685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	&sda_temp_input[X].dev_attr.attr,	\
54785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	&sda_temp_max[X].dev_attr.attr,		\
54885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	&sda_temp_max_hyst[X].dev_attr.attr
54985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
55085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define PWM_UNIT_ATTRS(X)	\
55185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	&sda_pwm[X].dev_attr.attr,		\
55285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	&sda_pwm_mode[X].dev_attr.attr,		\
55385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	&sda_pwm_enable[X].dev_attr.attr
55485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
55585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo#define TOLERANCE_UNIT_ATTRS(X)	\
55685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	&sda_tolerance[X].dev_attr.attr
55785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
55885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct attribute *w83l786ng_attributes[] = {
55985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	IN_UNIT_ATTRS(0),
56085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	IN_UNIT_ATTRS(1),
56185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	IN_UNIT_ATTRS(2),
56285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	FAN_UNIT_ATTRS(0),
56385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	FAN_UNIT_ATTRS(1),
56485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	TEMP_UNIT_ATTRS(0),
56585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	TEMP_UNIT_ATTRS(1),
56685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	PWM_UNIT_ATTRS(0),
56785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	PWM_UNIT_ATTRS(1),
56885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	TOLERANCE_UNIT_ATTRS(0),
56985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	TOLERANCE_UNIT_ATTRS(1),
57085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	NULL
57185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo};
57285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
57385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic const struct attribute_group w83l786ng_group = {
57485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	.attrs = w83l786ng_attributes,
57585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo};
57685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
57785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic int
57885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Low83l786ng_attach_adapter(struct i2c_adapter *adapter)
57985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
58085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	if (!(adapter->class & I2C_CLASS_HWMON))
58185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		return 0;
58285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return i2c_probe(adapter, &addr_data, w83l786ng_detect);
58385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
58485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
58585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic int
58685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Low83l786ng_detect(struct i2c_adapter *adapter, int address, int kind)
58785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
58885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct i2c_client *client;
58985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct device *dev;
59085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct w83l786ng_data *data;
59185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int i, err = 0;
59285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 reg_tmp;
59385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
59485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
59585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		goto exit;
59685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	}
59785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
59885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	/* OK. For now, we presume we have a valid client. We now create the
59985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	   client structure, even though we cannot fill it completely yet.
60085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	   But it allows us to access w83l786ng_{read,write}_value. */
60185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
60285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	if (!(data = kzalloc(sizeof(struct w83l786ng_data), GFP_KERNEL))) {
60385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		err = -ENOMEM;
60485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		goto exit;
60585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	}
60685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
60785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	client = &data->client;
60885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	dev = &client->dev;
60985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	i2c_set_clientdata(client, data);
61085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	client->addr = address;
61185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	client->adapter = adapter;
61285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	client->driver = &w83l786ng_driver;
61385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
61485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	/*
61585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	 * Now we do the remaining detection. A negative kind means that
61685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	 * the driver was loaded with no force parameter (default), so we
61785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	 * must both detect and identify the chip (actually there is only
61885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	 * one possible kind of chip for now, W83L786NG). A zero kind means
61985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	 * that the driver was loaded with the force parameter, the detection
62085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	 * step shall be skipped. A positive kind means that the driver
62185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	 * was loaded with the force parameter and a given kind of chip is
62285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	 * requested, so both the detection and the identification steps
62385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	 * are skipped.
62485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	 */
62585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	if (kind < 0) { /* detection */
62685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		if (((w83l786ng_read_value(client,
62785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		    W83L786NG_REG_CONFIG) & 0x80) != 0x00)) {
62885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			dev_dbg(&adapter->dev,
62985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo				"W83L786NG detection failed at 0x%02x.\n",
63085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo				address);
63185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			goto exit_free;
63285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		}
63385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	}
63485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
63585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	if (kind <= 0) { /* identification */
63685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		u16 man_id;
63785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		u8 chip_id;
63885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
63985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		man_id = (w83l786ng_read_value(client,
64085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		    W83L786NG_REG_MAN_ID1) << 8) +
64185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		    w83l786ng_read_value(client, W83L786NG_REG_MAN_ID2);
64285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		chip_id = w83l786ng_read_value(client, W83L786NG_REG_CHIP_ID);
64385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
64485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		if (man_id == 0x5CA3) { /* Winbond */
64585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			if (chip_id == 0x80) { /* W83L786NG */
64685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo				kind = w83l786ng;
64785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			}
64885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		}
64985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
65085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		if (kind <= 0) { /* identification failed */
65185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			dev_info(&adapter->dev,
65285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			    "Unsupported chip (man_id=0x%04X, "
65385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			    "chip_id=0x%02X).\n", man_id, chip_id);
65485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			goto exit_free;
65585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		}
65685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	}
65785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
65885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	/* Fill in the remaining client fields and put into the global list */
65985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	strlcpy(client->name, "w83l786ng", I2C_NAME_SIZE);
66085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_init(&data->update_lock);
66185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
66285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	/* Tell the I2C layer a new client has arrived */
66385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	if ((err = i2c_attach_client(client)))
66485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		goto exit_free;
66585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
66685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	/* Initialize the chip */
66785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	w83l786ng_init_client(client);
66885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
66985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	/* A few vars need to be filled upon startup */
67085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	for (i = 0; i < 2; i++) {
67185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		data->fan_min[i] = w83l786ng_read_value(client,
67285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		    W83L786NG_REG_FAN_MIN(i));
67385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	}
67485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
67585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	/* Update the fan divisor */
67685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	reg_tmp = w83l786ng_read_value(client, W83L786NG_REG_FAN_DIV);
67785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	data->fan_div[0] = reg_tmp & 0x07;
67885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	data->fan_div[1] = (reg_tmp >> 4) & 0x07;
67985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
68085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	/* Register sysfs hooks */
68185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	if ((err = sysfs_create_group(&client->dev.kobj, &w83l786ng_group)))
68285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		goto exit_remove;
68385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
68485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	data->hwmon_dev = hwmon_device_register(dev);
68585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	if (IS_ERR(data->hwmon_dev)) {
68685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		err = PTR_ERR(data->hwmon_dev);
68785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		goto exit_remove;
68885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	}
68985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
69085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return 0;
69185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
69285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	/* Unregister sysfs hooks */
69385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
69485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loexit_remove:
69585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	sysfs_remove_group(&client->dev.kobj, &w83l786ng_group);
69685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	i2c_detach_client(client);
69785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loexit_free:
69885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	kfree(data);
69985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Loexit:
70085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return err;
70185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
70285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
70385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic int
70485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Low83l786ng_detach_client(struct i2c_client *client)
70585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
70685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct w83l786ng_data *data = i2c_get_clientdata(client);
70785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int err;
70885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
70985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	hwmon_device_unregister(data->hwmon_dev);
71085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	sysfs_remove_group(&client->dev.kobj, &w83l786ng_group);
71185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
71285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	if ((err = i2c_detach_client(client)))
71385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		return err;
71485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
71585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	kfree(data);
71685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
71785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return 0;
71885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
71985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
72085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic void
72185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Low83l786ng_init_client(struct i2c_client *client)
72285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
72385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 tmp;
72485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
72585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	if (reset)
72685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		w83l786ng_write_value(client, W83L786NG_REG_CONFIG, 0x80);
72785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
72885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	/* Start monitoring */
72985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	tmp = w83l786ng_read_value(client, W83L786NG_REG_CONFIG);
73085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	if (!(tmp & 0x01))
73185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		w83l786ng_write_value(client, W83L786NG_REG_CONFIG, tmp | 0x01);
73285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
73385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
73485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic struct w83l786ng_data *w83l786ng_update_device(struct device *dev)
73585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
73685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct i2c_client *client = to_i2c_client(dev);
73785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	struct w83l786ng_data *data = i2c_get_clientdata(client);
73885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	int i, j;
73985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	u8 reg_tmp, pwmcfg;
74085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
74185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_lock(&data->update_lock);
74285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
74385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	    || !data->valid) {
74485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		dev_dbg(&client->dev, "Updating w83l786ng data.\n");
74585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
74685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		/* Update the voltages measured value and limits */
74785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		for (i = 0; i < 3; i++) {
74885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			data->in[i] = w83l786ng_read_value(client,
74985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			    W83L786NG_REG_IN(i));
75085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			data->in_min[i] = w83l786ng_read_value(client,
75185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			    W83L786NG_REG_IN_MIN(i));
75285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			data->in_max[i] = w83l786ng_read_value(client,
75385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			    W83L786NG_REG_IN_MAX(i));
75485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		}
75585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
75685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		/* Update the fan counts and limits */
75785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		for (i = 0; i < 2; i++) {
75885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			data->fan[i] = w83l786ng_read_value(client,
75985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			    W83L786NG_REG_FAN(i));
76085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			data->fan_min[i] = w83l786ng_read_value(client,
76185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			    W83L786NG_REG_FAN_MIN(i));
76285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		}
76385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
76485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		/* Update the fan divisor */
76585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		reg_tmp = w83l786ng_read_value(client, W83L786NG_REG_FAN_DIV);
76685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		data->fan_div[0] = reg_tmp & 0x07;
76785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		data->fan_div[1] = (reg_tmp >> 4) & 0x07;
76885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
76985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		pwmcfg = w83l786ng_read_value(client, W83L786NG_REG_FAN_CFG);
77085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		for (i = 0; i < 2; i++) {
77185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			data->pwm_mode[i] =
77285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			    ((pwmcfg >> W83L786NG_PWM_MODE_SHIFT[i]) & 1)
77385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			    ? 0 : 1;
77485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			data->pwm_enable[i] =
77585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			    ((pwmcfg >> W83L786NG_PWM_ENABLE_SHIFT[i]) & 2) + 1;
77685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			data->pwm[i] = w83l786ng_read_value(client,
77785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			    W83L786NG_REG_PWM[i]);
77885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		}
77985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
78085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
78185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		/* Update the temperature sensors */
78285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		for (i = 0; i < 2; i++) {
78385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			for (j = 0; j < 3; j++) {
78485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo				data->temp[i][j] = w83l786ng_read_value(client,
78585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo				    W83L786NG_REG_TEMP[i][j]);
78685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo			}
78785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		}
78885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
78985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		/* Update Smart Fan I/II tolerance */
79085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		reg_tmp = w83l786ng_read_value(client, W83L786NG_REG_TOLERANCE);
79185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		data->tolerance[0] = reg_tmp & 0x0f;
79285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		data->tolerance[1] = (reg_tmp >> 4) & 0x0f;
79385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
79485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		data->last_updated = jiffies;
79585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo		data->valid = 1;
79685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
79785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	}
79885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
79985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	mutex_unlock(&data->update_lock);
80085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
80185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return data;
80285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
80385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
80485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic int __init
80585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Losensors_w83l786ng_init(void)
80685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
80785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	return i2c_add_driver(&w83l786ng_driver);
80885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
80985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
81085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lostatic void __exit
81185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Losensors_w83l786ng_exit(void)
81285f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo{
81385f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo	i2c_del_driver(&w83l786ng_driver);
81485f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo}
81585f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
81685f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin LoMODULE_AUTHOR("Kevin Lo");
81785f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin LoMODULE_DESCRIPTION("w83l786ng driver");
81885f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin LoMODULE_LICENSE("GPL");
81985f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lo
82085f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lomodule_init(sensors_w83l786ng_init);
82185f03bccd6e0e2ac6ccf017d4bcd5d74bb87a671Kevin Lomodule_exit(sensors_w83l786ng_exit);
822