1ff7924b0SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
285f03bccSKevin Lo /*
301d9def5SGuenter Roeck * w83l786ng.c - Linux kernel driver for hardware monitoring
401d9def5SGuenter Roeck * Copyright (c) 2007 Kevin Lo <kevlo@kevlo.org>
585f03bccSKevin Lo */
685f03bccSKevin Lo
785f03bccSKevin Lo /*
801d9def5SGuenter Roeck * Supports following chips:
901d9def5SGuenter Roeck *
1001d9def5SGuenter Roeck * Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA
1101d9def5SGuenter Roeck * w83l786ng 3 2 2 2 0x7b 0x5ca3 yes no
1285f03bccSKevin Lo */
1385f03bccSKevin Lo
1485f03bccSKevin Lo #include <linux/module.h>
1585f03bccSKevin Lo #include <linux/init.h>
1685f03bccSKevin Lo #include <linux/slab.h>
1785f03bccSKevin Lo #include <linux/i2c.h>
1885f03bccSKevin Lo #include <linux/hwmon.h>
1985f03bccSKevin Lo #include <linux/hwmon-sysfs.h>
2085f03bccSKevin Lo #include <linux/err.h>
2185f03bccSKevin Lo #include <linux/mutex.h>
22dcd8f392SJean Delvare #include <linux/jiffies.h>
2385f03bccSKevin Lo
2485f03bccSKevin Lo /* Addresses to scan */
2525e9c86dSMark M. Hoffman static const unsigned short normal_i2c[] = { 0x2e, 0x2f, I2C_CLIENT_END };
2685f03bccSKevin Lo
2785f03bccSKevin Lo /* Insmod parameters */
2885f03bccSKevin Lo
2990ab5ee9SRusty Russell static bool reset;
3085f03bccSKevin Lo module_param(reset, bool, 0);
3185f03bccSKevin Lo MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended");
3285f03bccSKevin Lo
3385f03bccSKevin Lo #define W83L786NG_REG_IN_MIN(nr) (0x2C + (nr) * 2)
3485f03bccSKevin Lo #define W83L786NG_REG_IN_MAX(nr) (0x2B + (nr) * 2)
3585f03bccSKevin Lo #define W83L786NG_REG_IN(nr) ((nr) + 0x20)
3685f03bccSKevin Lo
3785f03bccSKevin Lo #define W83L786NG_REG_FAN(nr) ((nr) + 0x28)
3885f03bccSKevin Lo #define W83L786NG_REG_FAN_MIN(nr) ((nr) + 0x3B)
3985f03bccSKevin Lo
4085f03bccSKevin Lo #define W83L786NG_REG_CONFIG 0x40
4185f03bccSKevin Lo #define W83L786NG_REG_ALARM1 0x41
4285f03bccSKevin Lo #define W83L786NG_REG_ALARM2 0x42
4385f03bccSKevin Lo #define W83L786NG_REG_GPIO_EN 0x47
4485f03bccSKevin Lo #define W83L786NG_REG_MAN_ID2 0x4C
4585f03bccSKevin Lo #define W83L786NG_REG_MAN_ID1 0x4D
4685f03bccSKevin Lo #define W83L786NG_REG_CHIP_ID 0x4E
4785f03bccSKevin Lo
4885f03bccSKevin Lo #define W83L786NG_REG_DIODE 0x53
4985f03bccSKevin Lo #define W83L786NG_REG_FAN_DIV 0x54
5085f03bccSKevin Lo #define W83L786NG_REG_FAN_CFG 0x80
5185f03bccSKevin Lo
5285f03bccSKevin Lo #define W83L786NG_REG_TOLERANCE 0x8D
5385f03bccSKevin Lo
5485f03bccSKevin Lo static const u8 W83L786NG_REG_TEMP[2][3] = {
5585f03bccSKevin Lo { 0x25, /* TEMP 0 in DataSheet */
5685f03bccSKevin Lo 0x35, /* TEMP 0 Over in DataSheet */
5785f03bccSKevin Lo 0x36 }, /* TEMP 0 Hyst in DataSheet */
5885f03bccSKevin Lo { 0x26, /* TEMP 1 in DataSheet */
5985f03bccSKevin Lo 0x37, /* TEMP 1 Over in DataSheet */
6085f03bccSKevin Lo 0x38 } /* TEMP 1 Hyst in DataSheet */
6185f03bccSKevin Lo };
6285f03bccSKevin Lo
6385f03bccSKevin Lo static const u8 W83L786NG_PWM_MODE_SHIFT[] = {6, 7};
6485f03bccSKevin Lo static const u8 W83L786NG_PWM_ENABLE_SHIFT[] = {2, 4};
6585f03bccSKevin Lo
6685f03bccSKevin Lo /* FAN Duty Cycle, be used to control */
6785f03bccSKevin Lo static const u8 W83L786NG_REG_PWM[] = {0x81, 0x87};
6885f03bccSKevin Lo
6985f03bccSKevin Lo
7085f03bccSKevin Lo static inline u8
FAN_TO_REG(long rpm,int div)7185f03bccSKevin Lo FAN_TO_REG(long rpm, int div)
7285f03bccSKevin Lo {
7385f03bccSKevin Lo if (rpm == 0)
7485f03bccSKevin Lo return 255;
752a844c14SGuenter Roeck rpm = clamp_val(rpm, 1, 1000000);
762a844c14SGuenter Roeck return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
7785f03bccSKevin Lo }
7885f03bccSKevin Lo
7985f03bccSKevin Lo #define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : \
8085f03bccSKevin Lo ((val) == 255 ? 0 : \
8185f03bccSKevin Lo 1350000 / ((val) * (div))))
8285f03bccSKevin Lo
8385f03bccSKevin Lo /* for temp */
842a844c14SGuenter Roeck #define TEMP_TO_REG(val) (clamp_val(((val) < 0 ? (val) + 0x100 * 1000 \
8585f03bccSKevin Lo : (val)) / 1000, 0, 0xff))
86ca3ccad8SGuenter Roeck #define TEMP_FROM_REG(val) (((val) & 0x80 ? \
87ca3ccad8SGuenter Roeck (val) - 0x100 : (val)) * 1000)
8885f03bccSKevin Lo
8901d9def5SGuenter Roeck /*
9001d9def5SGuenter Roeck * The analog voltage inputs have 8mV LSB. Since the sysfs output is
9101d9def5SGuenter Roeck * in mV as would be measured on the chip input pin, need to just
9201d9def5SGuenter Roeck * multiply/divide by 8 to translate from/to register values.
9301d9def5SGuenter Roeck */
942a844c14SGuenter Roeck #define IN_TO_REG(val) (clamp_val((((val) + 4) / 8), 0, 255))
9585f03bccSKevin Lo #define IN_FROM_REG(val) ((val) * 8)
9685f03bccSKevin Lo
9785f03bccSKevin Lo #define DIV_FROM_REG(val) (1 << (val))
9885f03bccSKevin Lo
9985f03bccSKevin Lo static inline u8
DIV_TO_REG(long val)10085f03bccSKevin Lo DIV_TO_REG(long val)
10185f03bccSKevin Lo {
10285f03bccSKevin Lo int i;
1032a844c14SGuenter Roeck val = clamp_val(val, 1, 128) >> 1;
10485f03bccSKevin Lo for (i = 0; i < 7; i++) {
10585f03bccSKevin Lo if (val == 0)
10685f03bccSKevin Lo break;
10785f03bccSKevin Lo val >>= 1;
10885f03bccSKevin Lo }
1097fe83ad8SFrans Meulenbroeks return (u8)i;
11085f03bccSKevin Lo }
11185f03bccSKevin Lo
11285f03bccSKevin Lo struct w83l786ng_data {
113202e4851SAxel Lin struct i2c_client *client;
11485f03bccSKevin Lo struct mutex update_lock;
115952a11caSPaul Fertser bool valid; /* true if following fields are valid */
11685f03bccSKevin Lo unsigned long last_updated; /* In jiffies */
11785f03bccSKevin Lo unsigned long last_nonvolatile; /* In jiffies, last time we update the
11801d9def5SGuenter Roeck * nonvolatile registers */
11985f03bccSKevin Lo
12085f03bccSKevin Lo u8 in[3];
12185f03bccSKevin Lo u8 in_max[3];
12285f03bccSKevin Lo u8 in_min[3];
12385f03bccSKevin Lo u8 fan[2];
12485f03bccSKevin Lo u8 fan_div[2];
12585f03bccSKevin Lo u8 fan_min[2];
12685f03bccSKevin Lo u8 temp_type[2];
12785f03bccSKevin Lo u8 temp[2][3];
12885f03bccSKevin Lo u8 pwm[2];
12985f03bccSKevin Lo u8 pwm_mode[2]; /* 0->DC variable voltage
13001d9def5SGuenter Roeck * 1->PWM variable duty cycle */
13185f03bccSKevin Lo
13285f03bccSKevin Lo u8 pwm_enable[2]; /* 1->manual
13301d9def5SGuenter Roeck * 2->thermal cruise (also called SmartFan I) */
13485f03bccSKevin Lo u8 tolerance[2];
13585f03bccSKevin Lo };
13685f03bccSKevin Lo
13785f03bccSKevin Lo static u8
w83l786ng_read_value(struct i2c_client * client,u8 reg)13885f03bccSKevin Lo w83l786ng_read_value(struct i2c_client *client, u8 reg)
13985f03bccSKevin Lo {
14085f03bccSKevin Lo return i2c_smbus_read_byte_data(client, reg);
14185f03bccSKevin Lo }
14285f03bccSKevin Lo
14385f03bccSKevin Lo static int
w83l786ng_write_value(struct i2c_client * client,u8 reg,u8 value)14485f03bccSKevin Lo w83l786ng_write_value(struct i2c_client *client, u8 reg, u8 value)
14585f03bccSKevin Lo {
14685f03bccSKevin Lo return i2c_smbus_write_byte_data(client, reg, value);
14785f03bccSKevin Lo }
14885f03bccSKevin Lo
w83l786ng_update_device(struct device * dev)149bab711aeSAxel Lin static struct w83l786ng_data *w83l786ng_update_device(struct device *dev)
150bab711aeSAxel Lin {
151202e4851SAxel Lin struct w83l786ng_data *data = dev_get_drvdata(dev);
152202e4851SAxel Lin struct i2c_client *client = data->client;
153bab711aeSAxel Lin int i, j;
154bab711aeSAxel Lin u8 reg_tmp, pwmcfg;
155bab711aeSAxel Lin
156bab711aeSAxel Lin mutex_lock(&data->update_lock);
157bab711aeSAxel Lin if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
158bab711aeSAxel Lin || !data->valid) {
159bab711aeSAxel Lin dev_dbg(&client->dev, "Updating w83l786ng data.\n");
160bab711aeSAxel Lin
161bab711aeSAxel Lin /* Update the voltages measured value and limits */
162bab711aeSAxel Lin for (i = 0; i < 3; i++) {
163bab711aeSAxel Lin data->in[i] = w83l786ng_read_value(client,
164bab711aeSAxel Lin W83L786NG_REG_IN(i));
165bab711aeSAxel Lin data->in_min[i] = w83l786ng_read_value(client,
166bab711aeSAxel Lin W83L786NG_REG_IN_MIN(i));
167bab711aeSAxel Lin data->in_max[i] = w83l786ng_read_value(client,
168bab711aeSAxel Lin W83L786NG_REG_IN_MAX(i));
169bab711aeSAxel Lin }
170bab711aeSAxel Lin
171bab711aeSAxel Lin /* Update the fan counts and limits */
172bab711aeSAxel Lin for (i = 0; i < 2; i++) {
173bab711aeSAxel Lin data->fan[i] = w83l786ng_read_value(client,
174bab711aeSAxel Lin W83L786NG_REG_FAN(i));
175bab711aeSAxel Lin data->fan_min[i] = w83l786ng_read_value(client,
176bab711aeSAxel Lin W83L786NG_REG_FAN_MIN(i));
177bab711aeSAxel Lin }
178bab711aeSAxel Lin
179bab711aeSAxel Lin /* Update the fan divisor */
180bab711aeSAxel Lin reg_tmp = w83l786ng_read_value(client, W83L786NG_REG_FAN_DIV);
181bab711aeSAxel Lin data->fan_div[0] = reg_tmp & 0x07;
182bab711aeSAxel Lin data->fan_div[1] = (reg_tmp >> 4) & 0x07;
183bab711aeSAxel Lin
184bab711aeSAxel Lin pwmcfg = w83l786ng_read_value(client, W83L786NG_REG_FAN_CFG);
185bab711aeSAxel Lin for (i = 0; i < 2; i++) {
186bab711aeSAxel Lin data->pwm_mode[i] =
187bab711aeSAxel Lin ((pwmcfg >> W83L786NG_PWM_MODE_SHIFT[i]) & 1)
188bab711aeSAxel Lin ? 0 : 1;
189bab711aeSAxel Lin data->pwm_enable[i] =
190bab711aeSAxel Lin ((pwmcfg >> W83L786NG_PWM_ENABLE_SHIFT[i]) & 3) + 1;
191bab711aeSAxel Lin data->pwm[i] =
192bab711aeSAxel Lin (w83l786ng_read_value(client, W83L786NG_REG_PWM[i])
193bab711aeSAxel Lin & 0x0f) * 0x11;
194bab711aeSAxel Lin }
195bab711aeSAxel Lin
196bab711aeSAxel Lin
197bab711aeSAxel Lin /* Update the temperature sensors */
198bab711aeSAxel Lin for (i = 0; i < 2; i++) {
199bab711aeSAxel Lin for (j = 0; j < 3; j++) {
200bab711aeSAxel Lin data->temp[i][j] = w83l786ng_read_value(client,
201bab711aeSAxel Lin W83L786NG_REG_TEMP[i][j]);
202bab711aeSAxel Lin }
203bab711aeSAxel Lin }
204bab711aeSAxel Lin
205bab711aeSAxel Lin /* Update Smart Fan I/II tolerance */
206bab711aeSAxel Lin reg_tmp = w83l786ng_read_value(client, W83L786NG_REG_TOLERANCE);
207bab711aeSAxel Lin data->tolerance[0] = reg_tmp & 0x0f;
208bab711aeSAxel Lin data->tolerance[1] = (reg_tmp >> 4) & 0x0f;
209bab711aeSAxel Lin
210bab711aeSAxel Lin data->last_updated = jiffies;
211952a11caSPaul Fertser data->valid = true;
212bab711aeSAxel Lin
213bab711aeSAxel Lin }
214bab711aeSAxel Lin
215bab711aeSAxel Lin mutex_unlock(&data->update_lock);
216bab711aeSAxel Lin
217bab711aeSAxel Lin return data;
218bab711aeSAxel Lin }
219bab711aeSAxel Lin
22085f03bccSKevin Lo /* following are the sysfs callback functions */
22185f03bccSKevin Lo #define show_in_reg(reg) \
22285f03bccSKevin Lo static ssize_t \
22385f03bccSKevin Lo show_##reg(struct device *dev, struct device_attribute *attr, \
22485f03bccSKevin Lo char *buf) \
22585f03bccSKevin Lo { \
22685f03bccSKevin Lo int nr = to_sensor_dev_attr(attr)->index; \
22785f03bccSKevin Lo struct w83l786ng_data *data = w83l786ng_update_device(dev); \
22885f03bccSKevin Lo return sprintf(buf, "%d\n", IN_FROM_REG(data->reg[nr])); \
22985f03bccSKevin Lo }
23085f03bccSKevin Lo
23185f03bccSKevin Lo show_in_reg(in)
23285f03bccSKevin Lo show_in_reg(in_min)
23385f03bccSKevin Lo show_in_reg(in_max)
23485f03bccSKevin Lo
23585f03bccSKevin Lo #define store_in_reg(REG, reg) \
23685f03bccSKevin Lo static ssize_t \
23785f03bccSKevin Lo store_in_##reg(struct device *dev, struct device_attribute *attr, \
23885f03bccSKevin Lo const char *buf, size_t count) \
23985f03bccSKevin Lo { \
24085f03bccSKevin Lo int nr = to_sensor_dev_attr(attr)->index; \
241202e4851SAxel Lin struct w83l786ng_data *data = dev_get_drvdata(dev); \
242202e4851SAxel Lin struct i2c_client *client = data->client; \
243ca3ccad8SGuenter Roeck unsigned long val; \
244ca3ccad8SGuenter Roeck int err = kstrtoul(buf, 10, &val); \
245ca3ccad8SGuenter Roeck if (err) \
246ca3ccad8SGuenter Roeck return err; \
24785f03bccSKevin Lo mutex_lock(&data->update_lock); \
24885f03bccSKevin Lo data->in_##reg[nr] = IN_TO_REG(val); \
24985f03bccSKevin Lo w83l786ng_write_value(client, W83L786NG_REG_IN_##REG(nr), \
25085f03bccSKevin Lo data->in_##reg[nr]); \
25185f03bccSKevin Lo mutex_unlock(&data->update_lock); \
25285f03bccSKevin Lo return count; \
25385f03bccSKevin Lo }
25485f03bccSKevin Lo
25585f03bccSKevin Lo store_in_reg(MIN, min)
25685f03bccSKevin Lo store_in_reg(MAX, max)
25785f03bccSKevin Lo
25885f03bccSKevin Lo static struct sensor_device_attribute sda_in_input[] = {
25985f03bccSKevin Lo SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0),
26085f03bccSKevin Lo SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1),
26185f03bccSKevin Lo SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2),
26285f03bccSKevin Lo };
26385f03bccSKevin Lo
26485f03bccSKevin Lo static struct sensor_device_attribute sda_in_min[] = {
26585f03bccSKevin Lo SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0),
26685f03bccSKevin Lo SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1),
26785f03bccSKevin Lo SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2),
26885f03bccSKevin Lo };
26985f03bccSKevin Lo
27085f03bccSKevin Lo static struct sensor_device_attribute sda_in_max[] = {
27185f03bccSKevin Lo SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0),
27285f03bccSKevin Lo SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1),
27385f03bccSKevin Lo SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2),
27485f03bccSKevin Lo };
27585f03bccSKevin Lo
27685f03bccSKevin Lo #define show_fan_reg(reg) \
27785f03bccSKevin Lo static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
27885f03bccSKevin Lo char *buf) \
27985f03bccSKevin Lo { \
28085f03bccSKevin Lo int nr = to_sensor_dev_attr(attr)->index; \
28185f03bccSKevin Lo struct w83l786ng_data *data = w83l786ng_update_device(dev); \
28285f03bccSKevin Lo return sprintf(buf, "%d\n", \
283a8d4d82eSAxel Lin FAN_FROM_REG(data->reg[nr], DIV_FROM_REG(data->fan_div[nr]))); \
28485f03bccSKevin Lo }
28585f03bccSKevin Lo
28685f03bccSKevin Lo show_fan_reg(fan);
28785f03bccSKevin Lo show_fan_reg(fan_min);
28885f03bccSKevin Lo
28985f03bccSKevin Lo static ssize_t
store_fan_min(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)29085f03bccSKevin Lo store_fan_min(struct device *dev, struct device_attribute *attr,
29185f03bccSKevin Lo const char *buf, size_t count)
29285f03bccSKevin Lo {
29385f03bccSKevin Lo int nr = to_sensor_dev_attr(attr)->index;
294202e4851SAxel Lin struct w83l786ng_data *data = dev_get_drvdata(dev);
295202e4851SAxel Lin struct i2c_client *client = data->client;
296ca3ccad8SGuenter Roeck unsigned long val;
297ca3ccad8SGuenter Roeck int err;
29885f03bccSKevin Lo
299ca3ccad8SGuenter Roeck err = kstrtoul(buf, 10, &val);
300ca3ccad8SGuenter Roeck if (err)
301ca3ccad8SGuenter Roeck return err;
302ca3ccad8SGuenter Roeck
30385f03bccSKevin Lo mutex_lock(&data->update_lock);
30485f03bccSKevin Lo data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
30585f03bccSKevin Lo w83l786ng_write_value(client, W83L786NG_REG_FAN_MIN(nr),
30685f03bccSKevin Lo data->fan_min[nr]);
30785f03bccSKevin Lo mutex_unlock(&data->update_lock);
30885f03bccSKevin Lo
30985f03bccSKevin Lo return count;
31085f03bccSKevin Lo }
31185f03bccSKevin Lo
31285f03bccSKevin Lo static ssize_t
show_fan_div(struct device * dev,struct device_attribute * attr,char * buf)31385f03bccSKevin Lo show_fan_div(struct device *dev, struct device_attribute *attr,
31485f03bccSKevin Lo char *buf)
31585f03bccSKevin Lo {
31685f03bccSKevin Lo int nr = to_sensor_dev_attr(attr)->index;
31785f03bccSKevin Lo struct w83l786ng_data *data = w83l786ng_update_device(dev);
31885f03bccSKevin Lo return sprintf(buf, "%u\n", DIV_FROM_REG(data->fan_div[nr]));
31985f03bccSKevin Lo }
32085f03bccSKevin Lo
32101d9def5SGuenter Roeck /*
32201d9def5SGuenter Roeck * Note: we save and restore the fan minimum here, because its value is
32301d9def5SGuenter Roeck * determined in part by the fan divisor. This follows the principle of
32401d9def5SGuenter Roeck * least surprise; the user doesn't expect the fan minimum to change just
32501d9def5SGuenter Roeck * because the divisor changed.
32601d9def5SGuenter Roeck */
32785f03bccSKevin Lo static ssize_t
store_fan_div(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)32885f03bccSKevin Lo store_fan_div(struct device *dev, struct device_attribute *attr,
32985f03bccSKevin Lo const char *buf, size_t count)
33085f03bccSKevin Lo {
33185f03bccSKevin Lo int nr = to_sensor_dev_attr(attr)->index;
332202e4851SAxel Lin struct w83l786ng_data *data = dev_get_drvdata(dev);
333202e4851SAxel Lin struct i2c_client *client = data->client;
33485f03bccSKevin Lo
33585f03bccSKevin Lo unsigned long min;
33685f03bccSKevin Lo u8 tmp_fan_div;
33785f03bccSKevin Lo u8 fan_div_reg;
33885f03bccSKevin Lo u8 keep_mask = 0;
33985f03bccSKevin Lo u8 new_shift = 0;
34085f03bccSKevin Lo
341ca3ccad8SGuenter Roeck unsigned long val;
342ca3ccad8SGuenter Roeck int err;
343ca3ccad8SGuenter Roeck
344ca3ccad8SGuenter Roeck err = kstrtoul(buf, 10, &val);
345ca3ccad8SGuenter Roeck if (err)
346ca3ccad8SGuenter Roeck return err;
347ca3ccad8SGuenter Roeck
34885f03bccSKevin Lo /* Save fan_min */
34985f03bccSKevin Lo mutex_lock(&data->update_lock);
35085f03bccSKevin Lo min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]));
35185f03bccSKevin Lo
352ca3ccad8SGuenter Roeck data->fan_div[nr] = DIV_TO_REG(val);
35385f03bccSKevin Lo
35485f03bccSKevin Lo switch (nr) {
35585f03bccSKevin Lo case 0:
35685f03bccSKevin Lo keep_mask = 0xf8;
35785f03bccSKevin Lo new_shift = 0;
35885f03bccSKevin Lo break;
35985f03bccSKevin Lo case 1:
36085f03bccSKevin Lo keep_mask = 0x8f;
36185f03bccSKevin Lo new_shift = 4;
36285f03bccSKevin Lo break;
36385f03bccSKevin Lo }
36485f03bccSKevin Lo
36585f03bccSKevin Lo fan_div_reg = w83l786ng_read_value(client, W83L786NG_REG_FAN_DIV)
36685f03bccSKevin Lo & keep_mask;
36785f03bccSKevin Lo
36885f03bccSKevin Lo tmp_fan_div = (data->fan_div[nr] << new_shift) & ~keep_mask;
36985f03bccSKevin Lo
37085f03bccSKevin Lo w83l786ng_write_value(client, W83L786NG_REG_FAN_DIV,
37185f03bccSKevin Lo fan_div_reg | tmp_fan_div);
37285f03bccSKevin Lo
37385f03bccSKevin Lo /* Restore fan_min */
37485f03bccSKevin Lo data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
37585f03bccSKevin Lo w83l786ng_write_value(client, W83L786NG_REG_FAN_MIN(nr),
37685f03bccSKevin Lo data->fan_min[nr]);
37785f03bccSKevin Lo mutex_unlock(&data->update_lock);
37885f03bccSKevin Lo
37985f03bccSKevin Lo return count;
38085f03bccSKevin Lo }
38185f03bccSKevin Lo
38285f03bccSKevin Lo static struct sensor_device_attribute sda_fan_input[] = {
38385f03bccSKevin Lo SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0),
38485f03bccSKevin Lo SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1),
38585f03bccSKevin Lo };
38685f03bccSKevin Lo
38785f03bccSKevin Lo static struct sensor_device_attribute sda_fan_min[] = {
38885f03bccSKevin Lo SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
38985f03bccSKevin Lo store_fan_min, 0),
39085f03bccSKevin Lo SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min,
39185f03bccSKevin Lo store_fan_min, 1),
39285f03bccSKevin Lo };
39385f03bccSKevin Lo
39485f03bccSKevin Lo static struct sensor_device_attribute sda_fan_div[] = {
39585f03bccSKevin Lo SENSOR_ATTR(fan1_div, S_IWUSR | S_IRUGO, show_fan_div,
39685f03bccSKevin Lo store_fan_div, 0),
39785f03bccSKevin Lo SENSOR_ATTR(fan2_div, S_IWUSR | S_IRUGO, show_fan_div,
39885f03bccSKevin Lo store_fan_div, 1),
39985f03bccSKevin Lo };
40085f03bccSKevin Lo
40185f03bccSKevin Lo
40285f03bccSKevin Lo /* read/write the temperature, includes measured value and limits */
40385f03bccSKevin Lo
40485f03bccSKevin Lo static ssize_t
show_temp(struct device * dev,struct device_attribute * attr,char * buf)40585f03bccSKevin Lo show_temp(struct device *dev, struct device_attribute *attr, char *buf)
40685f03bccSKevin Lo {
40785f03bccSKevin Lo struct sensor_device_attribute_2 *sensor_attr =
40885f03bccSKevin Lo to_sensor_dev_attr_2(attr);
40985f03bccSKevin Lo int nr = sensor_attr->nr;
41085f03bccSKevin Lo int index = sensor_attr->index;
41185f03bccSKevin Lo struct w83l786ng_data *data = w83l786ng_update_device(dev);
41285f03bccSKevin Lo return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr][index]));
41385f03bccSKevin Lo }
41485f03bccSKevin Lo
41585f03bccSKevin Lo static ssize_t
store_temp(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)41685f03bccSKevin Lo store_temp(struct device *dev, struct device_attribute *attr,
41785f03bccSKevin Lo const char *buf, size_t count)
41885f03bccSKevin Lo {
41985f03bccSKevin Lo struct sensor_device_attribute_2 *sensor_attr =
42085f03bccSKevin Lo to_sensor_dev_attr_2(attr);
42185f03bccSKevin Lo int nr = sensor_attr->nr;
42285f03bccSKevin Lo int index = sensor_attr->index;
423202e4851SAxel Lin struct w83l786ng_data *data = dev_get_drvdata(dev);
424202e4851SAxel Lin struct i2c_client *client = data->client;
425ca3ccad8SGuenter Roeck long val;
426ca3ccad8SGuenter Roeck int err;
42785f03bccSKevin Lo
428ca3ccad8SGuenter Roeck err = kstrtol(buf, 10, &val);
429ca3ccad8SGuenter Roeck if (err)
430ca3ccad8SGuenter Roeck return err;
431ca3ccad8SGuenter Roeck
43285f03bccSKevin Lo mutex_lock(&data->update_lock);
43385f03bccSKevin Lo data->temp[nr][index] = TEMP_TO_REG(val);
43485f03bccSKevin Lo w83l786ng_write_value(client, W83L786NG_REG_TEMP[nr][index],
43585f03bccSKevin Lo data->temp[nr][index]);
43685f03bccSKevin Lo mutex_unlock(&data->update_lock);
43785f03bccSKevin Lo
43885f03bccSKevin Lo return count;
43985f03bccSKevin Lo }
44085f03bccSKevin Lo
44185f03bccSKevin Lo static struct sensor_device_attribute_2 sda_temp_input[] = {
44285f03bccSKevin Lo SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0),
44385f03bccSKevin Lo SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 1, 0),
44485f03bccSKevin Lo };
44585f03bccSKevin Lo
44685f03bccSKevin Lo static struct sensor_device_attribute_2 sda_temp_max[] = {
44785f03bccSKevin Lo SENSOR_ATTR_2(temp1_max, S_IRUGO | S_IWUSR,
44885f03bccSKevin Lo show_temp, store_temp, 0, 1),
44985f03bccSKevin Lo SENSOR_ATTR_2(temp2_max, S_IRUGO | S_IWUSR,
45085f03bccSKevin Lo show_temp, store_temp, 1, 1),
45185f03bccSKevin Lo };
45285f03bccSKevin Lo
45385f03bccSKevin Lo static struct sensor_device_attribute_2 sda_temp_max_hyst[] = {
45485f03bccSKevin Lo SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR,
45585f03bccSKevin Lo show_temp, store_temp, 0, 2),
45685f03bccSKevin Lo SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR,
45785f03bccSKevin Lo show_temp, store_temp, 1, 2),
45885f03bccSKevin Lo };
45985f03bccSKevin Lo
46085f03bccSKevin Lo #define show_pwm_reg(reg) \
46185f03bccSKevin Lo static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
46285f03bccSKevin Lo char *buf) \
46385f03bccSKevin Lo { \
46485f03bccSKevin Lo struct w83l786ng_data *data = w83l786ng_update_device(dev); \
46585f03bccSKevin Lo int nr = to_sensor_dev_attr(attr)->index; \
46685f03bccSKevin Lo return sprintf(buf, "%d\n", data->reg[nr]); \
46785f03bccSKevin Lo }
46885f03bccSKevin Lo
46985f03bccSKevin Lo show_pwm_reg(pwm_mode)
show_pwm_reg(pwm_enable)47085f03bccSKevin Lo show_pwm_reg(pwm_enable)
47185f03bccSKevin Lo show_pwm_reg(pwm)
47285f03bccSKevin Lo
47385f03bccSKevin Lo static ssize_t
47485f03bccSKevin Lo store_pwm_mode(struct device *dev, struct device_attribute *attr,
47585f03bccSKevin Lo const char *buf, size_t count)
47685f03bccSKevin Lo {
47785f03bccSKevin Lo int nr = to_sensor_dev_attr(attr)->index;
478202e4851SAxel Lin struct w83l786ng_data *data = dev_get_drvdata(dev);
479202e4851SAxel Lin struct i2c_client *client = data->client;
48085f03bccSKevin Lo u8 reg;
481ca3ccad8SGuenter Roeck unsigned long val;
482ca3ccad8SGuenter Roeck int err;
483ca3ccad8SGuenter Roeck
484ca3ccad8SGuenter Roeck err = kstrtoul(buf, 10, &val);
485ca3ccad8SGuenter Roeck if (err)
486ca3ccad8SGuenter Roeck return err;
48785f03bccSKevin Lo
48885f03bccSKevin Lo if (val > 1)
48985f03bccSKevin Lo return -EINVAL;
49085f03bccSKevin Lo mutex_lock(&data->update_lock);
49185f03bccSKevin Lo data->pwm_mode[nr] = val;
49285f03bccSKevin Lo reg = w83l786ng_read_value(client, W83L786NG_REG_FAN_CFG);
49385f03bccSKevin Lo reg &= ~(1 << W83L786NG_PWM_MODE_SHIFT[nr]);
49485f03bccSKevin Lo if (!val)
49585f03bccSKevin Lo reg |= 1 << W83L786NG_PWM_MODE_SHIFT[nr];
49685f03bccSKevin Lo w83l786ng_write_value(client, W83L786NG_REG_FAN_CFG, reg);
49785f03bccSKevin Lo mutex_unlock(&data->update_lock);
49885f03bccSKevin Lo return count;
49985f03bccSKevin Lo }
50085f03bccSKevin Lo
50185f03bccSKevin Lo static ssize_t
store_pwm(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)50285f03bccSKevin Lo store_pwm(struct device *dev, struct device_attribute *attr,
50385f03bccSKevin Lo const char *buf, size_t count)
50485f03bccSKevin Lo {
50585f03bccSKevin Lo int nr = to_sensor_dev_attr(attr)->index;
506202e4851SAxel Lin struct w83l786ng_data *data = dev_get_drvdata(dev);
507202e4851SAxel Lin struct i2c_client *client = data->client;
508ca3ccad8SGuenter Roeck unsigned long val;
509ca3ccad8SGuenter Roeck int err;
510ca3ccad8SGuenter Roeck
511ca3ccad8SGuenter Roeck err = kstrtoul(buf, 10, &val);
512ca3ccad8SGuenter Roeck if (err)
513ca3ccad8SGuenter Roeck return err;
5142a844c14SGuenter Roeck val = clamp_val(val, 0, 255);
51533a7ab91SJean Delvare val = DIV_ROUND_CLOSEST(val, 0x11);
51685f03bccSKevin Lo
51785f03bccSKevin Lo mutex_lock(&data->update_lock);
51833a7ab91SJean Delvare data->pwm[nr] = val * 0x11;
51933a7ab91SJean Delvare val |= w83l786ng_read_value(client, W83L786NG_REG_PWM[nr]) & 0xf0;
52085f03bccSKevin Lo w83l786ng_write_value(client, W83L786NG_REG_PWM[nr], val);
52185f03bccSKevin Lo mutex_unlock(&data->update_lock);
52285f03bccSKevin Lo return count;
52385f03bccSKevin Lo }
52485f03bccSKevin Lo
52585f03bccSKevin Lo static ssize_t
store_pwm_enable(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)52685f03bccSKevin Lo store_pwm_enable(struct device *dev, struct device_attribute *attr,
52785f03bccSKevin Lo const char *buf, size_t count)
52885f03bccSKevin Lo {
52985f03bccSKevin Lo int nr = to_sensor_dev_attr(attr)->index;
530202e4851SAxel Lin struct w83l786ng_data *data = dev_get_drvdata(dev);
531202e4851SAxel Lin struct i2c_client *client = data->client;
53285f03bccSKevin Lo u8 reg;
533ca3ccad8SGuenter Roeck unsigned long val;
534ca3ccad8SGuenter Roeck int err;
53585f03bccSKevin Lo
536ca3ccad8SGuenter Roeck err = kstrtoul(buf, 10, &val);
537ca3ccad8SGuenter Roeck if (err)
538ca3ccad8SGuenter Roeck return err;
539ca3ccad8SGuenter Roeck
540ca3ccad8SGuenter Roeck if (!val || val > 2) /* only modes 1 and 2 are supported */
54185f03bccSKevin Lo return -EINVAL;
54285f03bccSKevin Lo
54385f03bccSKevin Lo mutex_lock(&data->update_lock);
54485f03bccSKevin Lo reg = w83l786ng_read_value(client, W83L786NG_REG_FAN_CFG);
54585f03bccSKevin Lo data->pwm_enable[nr] = val;
546cf7559bcSBrian Carnes reg &= ~(0x03 << W83L786NG_PWM_ENABLE_SHIFT[nr]);
54785f03bccSKevin Lo reg |= (val - 1) << W83L786NG_PWM_ENABLE_SHIFT[nr];
54885f03bccSKevin Lo w83l786ng_write_value(client, W83L786NG_REG_FAN_CFG, reg);
54985f03bccSKevin Lo mutex_unlock(&data->update_lock);
55085f03bccSKevin Lo return count;
55185f03bccSKevin Lo }
55285f03bccSKevin Lo
55385f03bccSKevin Lo static struct sensor_device_attribute sda_pwm[] = {
55485f03bccSKevin Lo SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0),
55585f03bccSKevin Lo SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1),
55685f03bccSKevin Lo };
55785f03bccSKevin Lo
55885f03bccSKevin Lo static struct sensor_device_attribute sda_pwm_mode[] = {
55985f03bccSKevin Lo SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
56085f03bccSKevin Lo store_pwm_mode, 0),
56185f03bccSKevin Lo SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
56285f03bccSKevin Lo store_pwm_mode, 1),
56385f03bccSKevin Lo };
56485f03bccSKevin Lo
56585f03bccSKevin Lo static struct sensor_device_attribute sda_pwm_enable[] = {
56685f03bccSKevin Lo SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
56785f03bccSKevin Lo store_pwm_enable, 0),
56885f03bccSKevin Lo SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
56985f03bccSKevin Lo store_pwm_enable, 1),
57085f03bccSKevin Lo };
57185f03bccSKevin Lo
57285f03bccSKevin Lo /* For Smart Fan I/Thermal Cruise and Smart Fan II */
57385f03bccSKevin Lo static ssize_t
show_tolerance(struct device * dev,struct device_attribute * attr,char * buf)57485f03bccSKevin Lo show_tolerance(struct device *dev, struct device_attribute *attr, char *buf)
57585f03bccSKevin Lo {
57685f03bccSKevin Lo int nr = to_sensor_dev_attr(attr)->index;
57785f03bccSKevin Lo struct w83l786ng_data *data = w83l786ng_update_device(dev);
57885f03bccSKevin Lo return sprintf(buf, "%ld\n", (long)data->tolerance[nr]);
57985f03bccSKevin Lo }
58085f03bccSKevin Lo
58185f03bccSKevin Lo static ssize_t
store_tolerance(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)58285f03bccSKevin Lo store_tolerance(struct device *dev, struct device_attribute *attr,
58385f03bccSKevin Lo const char *buf, size_t count)
58485f03bccSKevin Lo {
58585f03bccSKevin Lo int nr = to_sensor_dev_attr(attr)->index;
586202e4851SAxel Lin struct w83l786ng_data *data = dev_get_drvdata(dev);
587202e4851SAxel Lin struct i2c_client *client = data->client;
58885f03bccSKevin Lo u8 tol_tmp, tol_mask;
589ca3ccad8SGuenter Roeck unsigned long val;
590ca3ccad8SGuenter Roeck int err;
59185f03bccSKevin Lo
592ca3ccad8SGuenter Roeck err = kstrtoul(buf, 10, &val);
593ca3ccad8SGuenter Roeck if (err)
594ca3ccad8SGuenter Roeck return err;
59585f03bccSKevin Lo
59685f03bccSKevin Lo mutex_lock(&data->update_lock);
59785f03bccSKevin Lo tol_mask = w83l786ng_read_value(client,
59885f03bccSKevin Lo W83L786NG_REG_TOLERANCE) & ((nr == 1) ? 0x0f : 0xf0);
5992a844c14SGuenter Roeck tol_tmp = clamp_val(val, 0, 15);
60085f03bccSKevin Lo tol_tmp &= 0x0f;
60185f03bccSKevin Lo data->tolerance[nr] = tol_tmp;
602ca3ccad8SGuenter Roeck if (nr == 1)
60385f03bccSKevin Lo tol_tmp <<= 4;
60485f03bccSKevin Lo
60585f03bccSKevin Lo w83l786ng_write_value(client, W83L786NG_REG_TOLERANCE,
60685f03bccSKevin Lo tol_mask | tol_tmp);
60785f03bccSKevin Lo mutex_unlock(&data->update_lock);
60885f03bccSKevin Lo return count;
60985f03bccSKevin Lo }
61085f03bccSKevin Lo
61185f03bccSKevin Lo static struct sensor_device_attribute sda_tolerance[] = {
61285f03bccSKevin Lo SENSOR_ATTR(pwm1_tolerance, S_IWUSR | S_IRUGO,
61385f03bccSKevin Lo show_tolerance, store_tolerance, 0),
61485f03bccSKevin Lo SENSOR_ATTR(pwm2_tolerance, S_IWUSR | S_IRUGO,
61585f03bccSKevin Lo show_tolerance, store_tolerance, 1),
61685f03bccSKevin Lo };
61785f03bccSKevin Lo
61885f03bccSKevin Lo
61985f03bccSKevin Lo #define IN_UNIT_ATTRS(X) \
62085f03bccSKevin Lo &sda_in_input[X].dev_attr.attr, \
62185f03bccSKevin Lo &sda_in_min[X].dev_attr.attr, \
62285f03bccSKevin Lo &sda_in_max[X].dev_attr.attr
62385f03bccSKevin Lo
62485f03bccSKevin Lo #define FAN_UNIT_ATTRS(X) \
62585f03bccSKevin Lo &sda_fan_input[X].dev_attr.attr, \
62685f03bccSKevin Lo &sda_fan_min[X].dev_attr.attr, \
62785f03bccSKevin Lo &sda_fan_div[X].dev_attr.attr
62885f03bccSKevin Lo
62985f03bccSKevin Lo #define TEMP_UNIT_ATTRS(X) \
63085f03bccSKevin Lo &sda_temp_input[X].dev_attr.attr, \
63185f03bccSKevin Lo &sda_temp_max[X].dev_attr.attr, \
63285f03bccSKevin Lo &sda_temp_max_hyst[X].dev_attr.attr
63385f03bccSKevin Lo
63485f03bccSKevin Lo #define PWM_UNIT_ATTRS(X) \
63585f03bccSKevin Lo &sda_pwm[X].dev_attr.attr, \
63685f03bccSKevin Lo &sda_pwm_mode[X].dev_attr.attr, \
63785f03bccSKevin Lo &sda_pwm_enable[X].dev_attr.attr
63885f03bccSKevin Lo
63985f03bccSKevin Lo #define TOLERANCE_UNIT_ATTRS(X) \
64085f03bccSKevin Lo &sda_tolerance[X].dev_attr.attr
64185f03bccSKevin Lo
642202e4851SAxel Lin static struct attribute *w83l786ng_attrs[] = {
64385f03bccSKevin Lo IN_UNIT_ATTRS(0),
64485f03bccSKevin Lo IN_UNIT_ATTRS(1),
64585f03bccSKevin Lo IN_UNIT_ATTRS(2),
64685f03bccSKevin Lo FAN_UNIT_ATTRS(0),
64785f03bccSKevin Lo FAN_UNIT_ATTRS(1),
64885f03bccSKevin Lo TEMP_UNIT_ATTRS(0),
64985f03bccSKevin Lo TEMP_UNIT_ATTRS(1),
65085f03bccSKevin Lo PWM_UNIT_ATTRS(0),
65185f03bccSKevin Lo PWM_UNIT_ATTRS(1),
65285f03bccSKevin Lo TOLERANCE_UNIT_ATTRS(0),
65385f03bccSKevin Lo TOLERANCE_UNIT_ATTRS(1),
65485f03bccSKevin Lo NULL
65585f03bccSKevin Lo };
65685f03bccSKevin Lo
657202e4851SAxel Lin ATTRIBUTE_GROUPS(w83l786ng);
65885f03bccSKevin Lo
65985f03bccSKevin Lo static int
w83l786ng_detect(struct i2c_client * client,struct i2c_board_info * info)660310ec792SJean Delvare w83l786ng_detect(struct i2c_client *client, struct i2c_board_info *info)
66185f03bccSKevin Lo {
66233468e76SJean Delvare struct i2c_adapter *adapter = client->adapter;
66352df6440SJean Delvare u16 man_id;
66452df6440SJean Delvare u8 chip_id;
66585f03bccSKevin Lo
666ca3ccad8SGuenter Roeck if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
66733468e76SJean Delvare return -ENODEV;
66885f03bccSKevin Lo
66952df6440SJean Delvare /* Detection */
67052df6440SJean Delvare if ((w83l786ng_read_value(client, W83L786NG_REG_CONFIG) & 0x80)) {
67152df6440SJean Delvare dev_dbg(&adapter->dev, "W83L786NG detection failed at 0x%02x\n",
67233468e76SJean Delvare client->addr);
67333468e76SJean Delvare return -ENODEV;
67485f03bccSKevin Lo }
67585f03bccSKevin Lo
67652df6440SJean Delvare /* Identification */
67752df6440SJean Delvare man_id = (w83l786ng_read_value(client, W83L786NG_REG_MAN_ID1) << 8) +
67885f03bccSKevin Lo w83l786ng_read_value(client, W83L786NG_REG_MAN_ID2);
67985f03bccSKevin Lo chip_id = w83l786ng_read_value(client, W83L786NG_REG_CHIP_ID);
68085f03bccSKevin Lo
68152df6440SJean Delvare if (man_id != 0x5CA3 || /* Winbond */
68252df6440SJean Delvare chip_id != 0x80) { /* W83L786NG */
68352df6440SJean Delvare dev_dbg(&adapter->dev,
68452df6440SJean Delvare "Unsupported chip (man_id=0x%04X, chip_id=0x%02X)\n",
68552df6440SJean Delvare man_id, chip_id);
68633468e76SJean Delvare return -ENODEV;
68785f03bccSKevin Lo }
68885f03bccSKevin Lo
689f2f394dbSWolfram Sang strscpy(info->type, "w83l786ng", I2C_NAME_SIZE);
69033468e76SJean Delvare
69133468e76SJean Delvare return 0;
69233468e76SJean Delvare }
69333468e76SJean Delvare
w83l786ng_init_client(struct i2c_client * client)694bab711aeSAxel Lin static void w83l786ng_init_client(struct i2c_client *client)
695bab711aeSAxel Lin {
696bab711aeSAxel Lin u8 tmp;
697bab711aeSAxel Lin
698bab711aeSAxel Lin if (reset)
699bab711aeSAxel Lin w83l786ng_write_value(client, W83L786NG_REG_CONFIG, 0x80);
700bab711aeSAxel Lin
701bab711aeSAxel Lin /* Start monitoring */
702bab711aeSAxel Lin tmp = w83l786ng_read_value(client, W83L786NG_REG_CONFIG);
703bab711aeSAxel Lin if (!(tmp & 0x01))
704bab711aeSAxel Lin w83l786ng_write_value(client, W83L786NG_REG_CONFIG, tmp | 0x01);
705bab711aeSAxel Lin }
706bab711aeSAxel Lin
70733468e76SJean Delvare static int
w83l786ng_probe(struct i2c_client * client)70867487038SStephen Kitt w83l786ng_probe(struct i2c_client *client)
70933468e76SJean Delvare {
71033468e76SJean Delvare struct device *dev = &client->dev;
71133468e76SJean Delvare struct w83l786ng_data *data;
712202e4851SAxel Lin struct device *hwmon_dev;
713202e4851SAxel Lin int i;
71433468e76SJean Delvare u8 reg_tmp;
71533468e76SJean Delvare
716202e4851SAxel Lin data = devm_kzalloc(dev, sizeof(struct w83l786ng_data), GFP_KERNEL);
717816864d9SGuenter Roeck if (!data)
718816864d9SGuenter Roeck return -ENOMEM;
71933468e76SJean Delvare
720202e4851SAxel Lin data->client = client;
72185f03bccSKevin Lo mutex_init(&data->update_lock);
72285f03bccSKevin Lo
72385f03bccSKevin Lo /* Initialize the chip */
72485f03bccSKevin Lo w83l786ng_init_client(client);
72585f03bccSKevin Lo
72685f03bccSKevin Lo /* A few vars need to be filled upon startup */
72785f03bccSKevin Lo for (i = 0; i < 2; i++) {
72885f03bccSKevin Lo data->fan_min[i] = w83l786ng_read_value(client,
72985f03bccSKevin Lo W83L786NG_REG_FAN_MIN(i));
73085f03bccSKevin Lo }
73185f03bccSKevin Lo
73285f03bccSKevin Lo /* Update the fan divisor */
73385f03bccSKevin Lo reg_tmp = w83l786ng_read_value(client, W83L786NG_REG_FAN_DIV);
73485f03bccSKevin Lo data->fan_div[0] = reg_tmp & 0x07;
73585f03bccSKevin Lo data->fan_div[1] = (reg_tmp >> 4) & 0x07;
73685f03bccSKevin Lo
737202e4851SAxel Lin hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
738202e4851SAxel Lin data,
739202e4851SAxel Lin w83l786ng_groups);
740202e4851SAxel Lin return PTR_ERR_OR_ZERO(hwmon_dev);
74185f03bccSKevin Lo }
74285f03bccSKevin Lo
743bab711aeSAxel Lin static const struct i2c_device_id w83l786ng_id[] = {
744bab711aeSAxel Lin { "w83l786ng", 0 },
745bab711aeSAxel Lin { }
746bab711aeSAxel Lin };
747bab711aeSAxel Lin MODULE_DEVICE_TABLE(i2c, w83l786ng_id);
74885f03bccSKevin Lo
749bab711aeSAxel Lin static struct i2c_driver w83l786ng_driver = {
750bab711aeSAxel Lin .class = I2C_CLASS_HWMON,
751bab711aeSAxel Lin .driver = {
752bab711aeSAxel Lin .name = "w83l786ng",
753bab711aeSAxel Lin },
754*1975d167SUwe Kleine-König .probe = w83l786ng_probe,
755bab711aeSAxel Lin .id_table = w83l786ng_id,
756bab711aeSAxel Lin .detect = w83l786ng_detect,
757bab711aeSAxel Lin .address_list = normal_i2c,
758bab711aeSAxel Lin };
75985f03bccSKevin Lo
760f0967eeaSAxel Lin module_i2c_driver(w83l786ng_driver);
76185f03bccSKevin Lo
76285f03bccSKevin Lo MODULE_AUTHOR("Kevin Lo");
76385f03bccSKevin Lo MODULE_DESCRIPTION("w83l786ng driver");
76485f03bccSKevin Lo MODULE_LICENSE("GPL");
765