18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * w83l786ng.c - Linux kernel driver for hardware monitoring
48c2ecf20Sopenharmony_ci * Copyright (c) 2007 Kevin Lo <kevlo@kevlo.org>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci/*
88c2ecf20Sopenharmony_ci * Supports following chips:
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Chip		#vin	#fanin	#pwm	#temp	wchipid	vendid	i2c	ISA
118c2ecf20Sopenharmony_ci * w83l786ng	3	2	2	2	0x7b	0x5ca3	yes	no
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/i2c.h>
188c2ecf20Sopenharmony_ci#include <linux/hwmon.h>
198c2ecf20Sopenharmony_ci#include <linux/hwmon-vid.h>
208c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h>
218c2ecf20Sopenharmony_ci#include <linux/err.h>
228c2ecf20Sopenharmony_ci#include <linux/mutex.h>
238c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* Addresses to scan */
268c2ecf20Sopenharmony_cistatic const unsigned short normal_i2c[] = { 0x2e, 0x2f, I2C_CLIENT_END };
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* Insmod parameters */
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic bool reset;
318c2ecf20Sopenharmony_cimodule_param(reset, bool, 0);
328c2ecf20Sopenharmony_ciMODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended");
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define W83L786NG_REG_IN_MIN(nr)	(0x2C + (nr) * 2)
358c2ecf20Sopenharmony_ci#define W83L786NG_REG_IN_MAX(nr)	(0x2B + (nr) * 2)
368c2ecf20Sopenharmony_ci#define W83L786NG_REG_IN(nr)		((nr) + 0x20)
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define W83L786NG_REG_FAN(nr)		((nr) + 0x28)
398c2ecf20Sopenharmony_ci#define W83L786NG_REG_FAN_MIN(nr)	((nr) + 0x3B)
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define W83L786NG_REG_CONFIG		0x40
428c2ecf20Sopenharmony_ci#define W83L786NG_REG_ALARM1		0x41
438c2ecf20Sopenharmony_ci#define W83L786NG_REG_ALARM2		0x42
448c2ecf20Sopenharmony_ci#define W83L786NG_REG_GPIO_EN		0x47
458c2ecf20Sopenharmony_ci#define W83L786NG_REG_MAN_ID2		0x4C
468c2ecf20Sopenharmony_ci#define W83L786NG_REG_MAN_ID1		0x4D
478c2ecf20Sopenharmony_ci#define W83L786NG_REG_CHIP_ID		0x4E
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define W83L786NG_REG_DIODE		0x53
508c2ecf20Sopenharmony_ci#define W83L786NG_REG_FAN_DIV		0x54
518c2ecf20Sopenharmony_ci#define W83L786NG_REG_FAN_CFG		0x80
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define W83L786NG_REG_TOLERANCE		0x8D
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic const u8 W83L786NG_REG_TEMP[2][3] = {
568c2ecf20Sopenharmony_ci	{ 0x25,		/* TEMP 0 in DataSheet */
578c2ecf20Sopenharmony_ci	  0x35,		/* TEMP 0 Over in DataSheet */
588c2ecf20Sopenharmony_ci	  0x36 },	/* TEMP 0 Hyst in DataSheet */
598c2ecf20Sopenharmony_ci	{ 0x26,		/* TEMP 1 in DataSheet */
608c2ecf20Sopenharmony_ci	  0x37,		/* TEMP 1 Over in DataSheet */
618c2ecf20Sopenharmony_ci	  0x38 }	/* TEMP 1 Hyst in DataSheet */
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic const u8 W83L786NG_PWM_MODE_SHIFT[] = {6, 7};
658c2ecf20Sopenharmony_cistatic const u8 W83L786NG_PWM_ENABLE_SHIFT[] = {2, 4};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/* FAN Duty Cycle, be used to control */
688c2ecf20Sopenharmony_cistatic const u8 W83L786NG_REG_PWM[] = {0x81, 0x87};
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic inline u8
728c2ecf20Sopenharmony_ciFAN_TO_REG(long rpm, int div)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	if (rpm == 0)
758c2ecf20Sopenharmony_ci		return 255;
768c2ecf20Sopenharmony_ci	rpm = clamp_val(rpm, 1, 1000000);
778c2ecf20Sopenharmony_ci	return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci#define FAN_FROM_REG(val, div)	((val) == 0   ? -1 : \
818c2ecf20Sopenharmony_ci				((val) == 255 ? 0 : \
828c2ecf20Sopenharmony_ci				1350000 / ((val) * (div))))
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/* for temp */
858c2ecf20Sopenharmony_ci#define TEMP_TO_REG(val)	(clamp_val(((val) < 0 ? (val) + 0x100 * 1000 \
868c2ecf20Sopenharmony_ci						      : (val)) / 1000, 0, 0xff))
878c2ecf20Sopenharmony_ci#define TEMP_FROM_REG(val)	(((val) & 0x80 ? \
888c2ecf20Sopenharmony_ci				  (val) - 0x100 : (val)) * 1000)
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/*
918c2ecf20Sopenharmony_ci * The analog voltage inputs have 8mV LSB. Since the sysfs output is
928c2ecf20Sopenharmony_ci * in mV as would be measured on the chip input pin, need to just
938c2ecf20Sopenharmony_ci * multiply/divide by 8 to translate from/to register values.
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_ci#define IN_TO_REG(val)		(clamp_val((((val) + 4) / 8), 0, 255))
968c2ecf20Sopenharmony_ci#define IN_FROM_REG(val)	((val) * 8)
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci#define DIV_FROM_REG(val)	(1 << (val))
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic inline u8
1018c2ecf20Sopenharmony_ciDIV_TO_REG(long val)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	int i;
1048c2ecf20Sopenharmony_ci	val = clamp_val(val, 1, 128) >> 1;
1058c2ecf20Sopenharmony_ci	for (i = 0; i < 7; i++) {
1068c2ecf20Sopenharmony_ci		if (val == 0)
1078c2ecf20Sopenharmony_ci			break;
1088c2ecf20Sopenharmony_ci		val >>= 1;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci	return (u8)i;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistruct w83l786ng_data {
1148c2ecf20Sopenharmony_ci	struct i2c_client *client;
1158c2ecf20Sopenharmony_ci	struct mutex update_lock;
1168c2ecf20Sopenharmony_ci	char valid;			/* !=0 if following fields are valid */
1178c2ecf20Sopenharmony_ci	unsigned long last_updated;	/* In jiffies */
1188c2ecf20Sopenharmony_ci	unsigned long last_nonvolatile;	/* In jiffies, last time we update the
1198c2ecf20Sopenharmony_ci					 * nonvolatile registers */
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	u8 in[3];
1228c2ecf20Sopenharmony_ci	u8 in_max[3];
1238c2ecf20Sopenharmony_ci	u8 in_min[3];
1248c2ecf20Sopenharmony_ci	u8 fan[2];
1258c2ecf20Sopenharmony_ci	u8 fan_div[2];
1268c2ecf20Sopenharmony_ci	u8 fan_min[2];
1278c2ecf20Sopenharmony_ci	u8 temp_type[2];
1288c2ecf20Sopenharmony_ci	u8 temp[2][3];
1298c2ecf20Sopenharmony_ci	u8 pwm[2];
1308c2ecf20Sopenharmony_ci	u8 pwm_mode[2];	/* 0->DC variable voltage
1318c2ecf20Sopenharmony_ci			 * 1->PWM variable duty cycle */
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	u8 pwm_enable[2]; /* 1->manual
1348c2ecf20Sopenharmony_ci			   * 2->thermal cruise (also called SmartFan I) */
1358c2ecf20Sopenharmony_ci	u8 tolerance[2];
1368c2ecf20Sopenharmony_ci};
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic u8
1398c2ecf20Sopenharmony_ciw83l786ng_read_value(struct i2c_client *client, u8 reg)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	return i2c_smbus_read_byte_data(client, reg);
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int
1458c2ecf20Sopenharmony_ciw83l786ng_write_value(struct i2c_client *client, u8 reg, u8 value)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	return i2c_smbus_write_byte_data(client, reg, value);
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic struct w83l786ng_data *w83l786ng_update_device(struct device *dev)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	struct w83l786ng_data *data = dev_get_drvdata(dev);
1538c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
1548c2ecf20Sopenharmony_ci	int i, j;
1558c2ecf20Sopenharmony_ci	u8 reg_tmp, pwmcfg;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
1588c2ecf20Sopenharmony_ci	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
1598c2ecf20Sopenharmony_ci	    || !data->valid) {
1608c2ecf20Sopenharmony_ci		dev_dbg(&client->dev, "Updating w83l786ng data.\n");
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci		/* Update the voltages measured value and limits */
1638c2ecf20Sopenharmony_ci		for (i = 0; i < 3; i++) {
1648c2ecf20Sopenharmony_ci			data->in[i] = w83l786ng_read_value(client,
1658c2ecf20Sopenharmony_ci			    W83L786NG_REG_IN(i));
1668c2ecf20Sopenharmony_ci			data->in_min[i] = w83l786ng_read_value(client,
1678c2ecf20Sopenharmony_ci			    W83L786NG_REG_IN_MIN(i));
1688c2ecf20Sopenharmony_ci			data->in_max[i] = w83l786ng_read_value(client,
1698c2ecf20Sopenharmony_ci			    W83L786NG_REG_IN_MAX(i));
1708c2ecf20Sopenharmony_ci		}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci		/* Update the fan counts and limits */
1738c2ecf20Sopenharmony_ci		for (i = 0; i < 2; i++) {
1748c2ecf20Sopenharmony_ci			data->fan[i] = w83l786ng_read_value(client,
1758c2ecf20Sopenharmony_ci			    W83L786NG_REG_FAN(i));
1768c2ecf20Sopenharmony_ci			data->fan_min[i] = w83l786ng_read_value(client,
1778c2ecf20Sopenharmony_ci			    W83L786NG_REG_FAN_MIN(i));
1788c2ecf20Sopenharmony_ci		}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci		/* Update the fan divisor */
1818c2ecf20Sopenharmony_ci		reg_tmp = w83l786ng_read_value(client, W83L786NG_REG_FAN_DIV);
1828c2ecf20Sopenharmony_ci		data->fan_div[0] = reg_tmp & 0x07;
1838c2ecf20Sopenharmony_ci		data->fan_div[1] = (reg_tmp >> 4) & 0x07;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		pwmcfg = w83l786ng_read_value(client, W83L786NG_REG_FAN_CFG);
1868c2ecf20Sopenharmony_ci		for (i = 0; i < 2; i++) {
1878c2ecf20Sopenharmony_ci			data->pwm_mode[i] =
1888c2ecf20Sopenharmony_ci			    ((pwmcfg >> W83L786NG_PWM_MODE_SHIFT[i]) & 1)
1898c2ecf20Sopenharmony_ci			    ? 0 : 1;
1908c2ecf20Sopenharmony_ci			data->pwm_enable[i] =
1918c2ecf20Sopenharmony_ci			    ((pwmcfg >> W83L786NG_PWM_ENABLE_SHIFT[i]) & 3) + 1;
1928c2ecf20Sopenharmony_ci			data->pwm[i] =
1938c2ecf20Sopenharmony_ci			    (w83l786ng_read_value(client, W83L786NG_REG_PWM[i])
1948c2ecf20Sopenharmony_ci			     & 0x0f) * 0x11;
1958c2ecf20Sopenharmony_ci		}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		/* Update the temperature sensors */
1998c2ecf20Sopenharmony_ci		for (i = 0; i < 2; i++) {
2008c2ecf20Sopenharmony_ci			for (j = 0; j < 3; j++) {
2018c2ecf20Sopenharmony_ci				data->temp[i][j] = w83l786ng_read_value(client,
2028c2ecf20Sopenharmony_ci				    W83L786NG_REG_TEMP[i][j]);
2038c2ecf20Sopenharmony_ci			}
2048c2ecf20Sopenharmony_ci		}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci		/* Update Smart Fan I/II tolerance */
2078c2ecf20Sopenharmony_ci		reg_tmp = w83l786ng_read_value(client, W83L786NG_REG_TOLERANCE);
2088c2ecf20Sopenharmony_ci		data->tolerance[0] = reg_tmp & 0x0f;
2098c2ecf20Sopenharmony_ci		data->tolerance[1] = (reg_tmp >> 4) & 0x0f;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci		data->last_updated = jiffies;
2128c2ecf20Sopenharmony_ci		data->valid = 1;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	return data;
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci/* following are the sysfs callback functions */
2228c2ecf20Sopenharmony_ci#define show_in_reg(reg) \
2238c2ecf20Sopenharmony_cistatic ssize_t \
2248c2ecf20Sopenharmony_cishow_##reg(struct device *dev, struct device_attribute *attr, \
2258c2ecf20Sopenharmony_ci	   char *buf) \
2268c2ecf20Sopenharmony_ci{ \
2278c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(attr)->index; \
2288c2ecf20Sopenharmony_ci	struct w83l786ng_data *data = w83l786ng_update_device(dev); \
2298c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", IN_FROM_REG(data->reg[nr])); \
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cishow_in_reg(in)
2338c2ecf20Sopenharmony_cishow_in_reg(in_min)
2348c2ecf20Sopenharmony_cishow_in_reg(in_max)
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci#define store_in_reg(REG, reg) \
2378c2ecf20Sopenharmony_cistatic ssize_t \
2388c2ecf20Sopenharmony_cistore_in_##reg(struct device *dev, struct device_attribute *attr, \
2398c2ecf20Sopenharmony_ci	       const char *buf, size_t count) \
2408c2ecf20Sopenharmony_ci{ \
2418c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(attr)->index; \
2428c2ecf20Sopenharmony_ci	struct w83l786ng_data *data = dev_get_drvdata(dev); \
2438c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client; \
2448c2ecf20Sopenharmony_ci	unsigned long val; \
2458c2ecf20Sopenharmony_ci	int err = kstrtoul(buf, 10, &val); \
2468c2ecf20Sopenharmony_ci	if (err) \
2478c2ecf20Sopenharmony_ci		return err; \
2488c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock); \
2498c2ecf20Sopenharmony_ci	data->in_##reg[nr] = IN_TO_REG(val); \
2508c2ecf20Sopenharmony_ci	w83l786ng_write_value(client, W83L786NG_REG_IN_##REG(nr), \
2518c2ecf20Sopenharmony_ci			      data->in_##reg[nr]); \
2528c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock); \
2538c2ecf20Sopenharmony_ci	return count; \
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistore_in_reg(MIN, min)
2578c2ecf20Sopenharmony_cistore_in_reg(MAX, max)
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic struct sensor_device_attribute sda_in_input[] = {
2608c2ecf20Sopenharmony_ci	SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0),
2618c2ecf20Sopenharmony_ci	SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1),
2628c2ecf20Sopenharmony_ci	SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2),
2638c2ecf20Sopenharmony_ci};
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic struct sensor_device_attribute sda_in_min[] = {
2668c2ecf20Sopenharmony_ci	SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0),
2678c2ecf20Sopenharmony_ci	SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1),
2688c2ecf20Sopenharmony_ci	SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2),
2698c2ecf20Sopenharmony_ci};
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic struct sensor_device_attribute sda_in_max[] = {
2728c2ecf20Sopenharmony_ci	SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0),
2738c2ecf20Sopenharmony_ci	SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1),
2748c2ecf20Sopenharmony_ci	SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2),
2758c2ecf20Sopenharmony_ci};
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci#define show_fan_reg(reg) \
2788c2ecf20Sopenharmony_cistatic ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
2798c2ecf20Sopenharmony_ci			  char *buf) \
2808c2ecf20Sopenharmony_ci{ \
2818c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(attr)->index; \
2828c2ecf20Sopenharmony_ci	struct w83l786ng_data *data = w83l786ng_update_device(dev); \
2838c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", \
2848c2ecf20Sopenharmony_ci		FAN_FROM_REG(data->reg[nr], DIV_FROM_REG(data->fan_div[nr]))); \
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cishow_fan_reg(fan);
2888c2ecf20Sopenharmony_cishow_fan_reg(fan_min);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic ssize_t
2918c2ecf20Sopenharmony_cistore_fan_min(struct device *dev, struct device_attribute *attr,
2928c2ecf20Sopenharmony_ci	      const char *buf, size_t count)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(attr)->index;
2958c2ecf20Sopenharmony_ci	struct w83l786ng_data *data = dev_get_drvdata(dev);
2968c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
2978c2ecf20Sopenharmony_ci	unsigned long val;
2988c2ecf20Sopenharmony_ci	int err;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	err = kstrtoul(buf, 10, &val);
3018c2ecf20Sopenharmony_ci	if (err)
3028c2ecf20Sopenharmony_ci		return err;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
3058c2ecf20Sopenharmony_ci	data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
3068c2ecf20Sopenharmony_ci	w83l786ng_write_value(client, W83L786NG_REG_FAN_MIN(nr),
3078c2ecf20Sopenharmony_ci			      data->fan_min[nr]);
3088c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	return count;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic ssize_t
3148c2ecf20Sopenharmony_cishow_fan_div(struct device *dev, struct device_attribute *attr,
3158c2ecf20Sopenharmony_ci	     char *buf)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(attr)->index;
3188c2ecf20Sopenharmony_ci	struct w83l786ng_data *data = w83l786ng_update_device(dev);
3198c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", DIV_FROM_REG(data->fan_div[nr]));
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci/*
3238c2ecf20Sopenharmony_ci * Note: we save and restore the fan minimum here, because its value is
3248c2ecf20Sopenharmony_ci * determined in part by the fan divisor.  This follows the principle of
3258c2ecf20Sopenharmony_ci * least surprise; the user doesn't expect the fan minimum to change just
3268c2ecf20Sopenharmony_ci * because the divisor changed.
3278c2ecf20Sopenharmony_ci */
3288c2ecf20Sopenharmony_cistatic ssize_t
3298c2ecf20Sopenharmony_cistore_fan_div(struct device *dev, struct device_attribute *attr,
3308c2ecf20Sopenharmony_ci	      const char *buf, size_t count)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(attr)->index;
3338c2ecf20Sopenharmony_ci	struct w83l786ng_data *data = dev_get_drvdata(dev);
3348c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	unsigned long min;
3378c2ecf20Sopenharmony_ci	u8 tmp_fan_div;
3388c2ecf20Sopenharmony_ci	u8 fan_div_reg;
3398c2ecf20Sopenharmony_ci	u8 keep_mask = 0;
3408c2ecf20Sopenharmony_ci	u8 new_shift = 0;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	unsigned long val;
3438c2ecf20Sopenharmony_ci	int err;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	err = kstrtoul(buf, 10, &val);
3468c2ecf20Sopenharmony_ci	if (err)
3478c2ecf20Sopenharmony_ci		return err;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/* Save fan_min */
3508c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
3518c2ecf20Sopenharmony_ci	min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]));
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	data->fan_div[nr] = DIV_TO_REG(val);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	switch (nr) {
3568c2ecf20Sopenharmony_ci	case 0:
3578c2ecf20Sopenharmony_ci		keep_mask = 0xf8;
3588c2ecf20Sopenharmony_ci		new_shift = 0;
3598c2ecf20Sopenharmony_ci		break;
3608c2ecf20Sopenharmony_ci	case 1:
3618c2ecf20Sopenharmony_ci		keep_mask = 0x8f;
3628c2ecf20Sopenharmony_ci		new_shift = 4;
3638c2ecf20Sopenharmony_ci		break;
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	fan_div_reg = w83l786ng_read_value(client, W83L786NG_REG_FAN_DIV)
3678c2ecf20Sopenharmony_ci					   & keep_mask;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	tmp_fan_div = (data->fan_div[nr] << new_shift) & ~keep_mask;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	w83l786ng_write_value(client, W83L786NG_REG_FAN_DIV,
3728c2ecf20Sopenharmony_ci			      fan_div_reg | tmp_fan_div);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	/* Restore fan_min */
3758c2ecf20Sopenharmony_ci	data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
3768c2ecf20Sopenharmony_ci	w83l786ng_write_value(client, W83L786NG_REG_FAN_MIN(nr),
3778c2ecf20Sopenharmony_ci			      data->fan_min[nr]);
3788c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	return count;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic struct sensor_device_attribute sda_fan_input[] = {
3848c2ecf20Sopenharmony_ci	SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0),
3858c2ecf20Sopenharmony_ci	SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1),
3868c2ecf20Sopenharmony_ci};
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic struct sensor_device_attribute sda_fan_min[] = {
3898c2ecf20Sopenharmony_ci	SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
3908c2ecf20Sopenharmony_ci		    store_fan_min, 0),
3918c2ecf20Sopenharmony_ci	SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min,
3928c2ecf20Sopenharmony_ci		    store_fan_min, 1),
3938c2ecf20Sopenharmony_ci};
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic struct sensor_device_attribute sda_fan_div[] = {
3968c2ecf20Sopenharmony_ci	SENSOR_ATTR(fan1_div, S_IWUSR | S_IRUGO, show_fan_div,
3978c2ecf20Sopenharmony_ci		    store_fan_div, 0),
3988c2ecf20Sopenharmony_ci	SENSOR_ATTR(fan2_div, S_IWUSR | S_IRUGO, show_fan_div,
3998c2ecf20Sopenharmony_ci		    store_fan_div, 1),
4008c2ecf20Sopenharmony_ci};
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci/* read/write the temperature, includes measured value and limits */
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cistatic ssize_t
4068c2ecf20Sopenharmony_cishow_temp(struct device *dev, struct device_attribute *attr, char *buf)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	struct sensor_device_attribute_2 *sensor_attr =
4098c2ecf20Sopenharmony_ci	    to_sensor_dev_attr_2(attr);
4108c2ecf20Sopenharmony_ci	int nr = sensor_attr->nr;
4118c2ecf20Sopenharmony_ci	int index = sensor_attr->index;
4128c2ecf20Sopenharmony_ci	struct w83l786ng_data *data = w83l786ng_update_device(dev);
4138c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr][index]));
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic ssize_t
4178c2ecf20Sopenharmony_cistore_temp(struct device *dev, struct device_attribute *attr,
4188c2ecf20Sopenharmony_ci	   const char *buf, size_t count)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	struct sensor_device_attribute_2 *sensor_attr =
4218c2ecf20Sopenharmony_ci	    to_sensor_dev_attr_2(attr);
4228c2ecf20Sopenharmony_ci	int nr = sensor_attr->nr;
4238c2ecf20Sopenharmony_ci	int index = sensor_attr->index;
4248c2ecf20Sopenharmony_ci	struct w83l786ng_data *data = dev_get_drvdata(dev);
4258c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
4268c2ecf20Sopenharmony_ci	long val;
4278c2ecf20Sopenharmony_ci	int err;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	err = kstrtol(buf, 10, &val);
4308c2ecf20Sopenharmony_ci	if (err)
4318c2ecf20Sopenharmony_ci		return err;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
4348c2ecf20Sopenharmony_ci	data->temp[nr][index] = TEMP_TO_REG(val);
4358c2ecf20Sopenharmony_ci	w83l786ng_write_value(client, W83L786NG_REG_TEMP[nr][index],
4368c2ecf20Sopenharmony_ci			      data->temp[nr][index]);
4378c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	return count;
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic struct sensor_device_attribute_2 sda_temp_input[] = {
4438c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0),
4448c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 1, 0),
4458c2ecf20Sopenharmony_ci};
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic struct sensor_device_attribute_2 sda_temp_max[] = {
4488c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(temp1_max, S_IRUGO | S_IWUSR,
4498c2ecf20Sopenharmony_ci		      show_temp, store_temp, 0, 1),
4508c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(temp2_max, S_IRUGO | S_IWUSR,
4518c2ecf20Sopenharmony_ci		      show_temp, store_temp, 1, 1),
4528c2ecf20Sopenharmony_ci};
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistatic struct sensor_device_attribute_2 sda_temp_max_hyst[] = {
4558c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR,
4568c2ecf20Sopenharmony_ci		      show_temp, store_temp, 0, 2),
4578c2ecf20Sopenharmony_ci	SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR,
4588c2ecf20Sopenharmony_ci		      show_temp, store_temp, 1, 2),
4598c2ecf20Sopenharmony_ci};
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci#define show_pwm_reg(reg) \
4628c2ecf20Sopenharmony_cistatic ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
4638c2ecf20Sopenharmony_ci			  char *buf) \
4648c2ecf20Sopenharmony_ci{ \
4658c2ecf20Sopenharmony_ci	struct w83l786ng_data *data = w83l786ng_update_device(dev); \
4668c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(attr)->index; \
4678c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", data->reg[nr]); \
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cishow_pwm_reg(pwm_mode)
4718c2ecf20Sopenharmony_cishow_pwm_reg(pwm_enable)
4728c2ecf20Sopenharmony_cishow_pwm_reg(pwm)
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cistatic ssize_t
4758c2ecf20Sopenharmony_cistore_pwm_mode(struct device *dev, struct device_attribute *attr,
4768c2ecf20Sopenharmony_ci	       const char *buf, size_t count)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(attr)->index;
4798c2ecf20Sopenharmony_ci	struct w83l786ng_data *data = dev_get_drvdata(dev);
4808c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
4818c2ecf20Sopenharmony_ci	u8 reg;
4828c2ecf20Sopenharmony_ci	unsigned long val;
4838c2ecf20Sopenharmony_ci	int err;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	err = kstrtoul(buf, 10, &val);
4868c2ecf20Sopenharmony_ci	if (err)
4878c2ecf20Sopenharmony_ci		return err;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	if (val > 1)
4908c2ecf20Sopenharmony_ci		return -EINVAL;
4918c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
4928c2ecf20Sopenharmony_ci	data->pwm_mode[nr] = val;
4938c2ecf20Sopenharmony_ci	reg = w83l786ng_read_value(client, W83L786NG_REG_FAN_CFG);
4948c2ecf20Sopenharmony_ci	reg &= ~(1 << W83L786NG_PWM_MODE_SHIFT[nr]);
4958c2ecf20Sopenharmony_ci	if (!val)
4968c2ecf20Sopenharmony_ci		reg |= 1 << W83L786NG_PWM_MODE_SHIFT[nr];
4978c2ecf20Sopenharmony_ci	w83l786ng_write_value(client, W83L786NG_REG_FAN_CFG, reg);
4988c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
4998c2ecf20Sopenharmony_ci	return count;
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic ssize_t
5038c2ecf20Sopenharmony_cistore_pwm(struct device *dev, struct device_attribute *attr,
5048c2ecf20Sopenharmony_ci	  const char *buf, size_t count)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(attr)->index;
5078c2ecf20Sopenharmony_ci	struct w83l786ng_data *data = dev_get_drvdata(dev);
5088c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
5098c2ecf20Sopenharmony_ci	unsigned long val;
5108c2ecf20Sopenharmony_ci	int err;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	err = kstrtoul(buf, 10, &val);
5138c2ecf20Sopenharmony_ci	if (err)
5148c2ecf20Sopenharmony_ci		return err;
5158c2ecf20Sopenharmony_ci	val = clamp_val(val, 0, 255);
5168c2ecf20Sopenharmony_ci	val = DIV_ROUND_CLOSEST(val, 0x11);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
5198c2ecf20Sopenharmony_ci	data->pwm[nr] = val * 0x11;
5208c2ecf20Sopenharmony_ci	val |= w83l786ng_read_value(client, W83L786NG_REG_PWM[nr]) & 0xf0;
5218c2ecf20Sopenharmony_ci	w83l786ng_write_value(client, W83L786NG_REG_PWM[nr], val);
5228c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
5238c2ecf20Sopenharmony_ci	return count;
5248c2ecf20Sopenharmony_ci}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_cistatic ssize_t
5278c2ecf20Sopenharmony_cistore_pwm_enable(struct device *dev, struct device_attribute *attr,
5288c2ecf20Sopenharmony_ci		 const char *buf, size_t count)
5298c2ecf20Sopenharmony_ci{
5308c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(attr)->index;
5318c2ecf20Sopenharmony_ci	struct w83l786ng_data *data = dev_get_drvdata(dev);
5328c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
5338c2ecf20Sopenharmony_ci	u8 reg;
5348c2ecf20Sopenharmony_ci	unsigned long val;
5358c2ecf20Sopenharmony_ci	int err;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	err = kstrtoul(buf, 10, &val);
5388c2ecf20Sopenharmony_ci	if (err)
5398c2ecf20Sopenharmony_ci		return err;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	if (!val || val > 2)  /* only modes 1 and 2 are supported */
5428c2ecf20Sopenharmony_ci		return -EINVAL;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
5458c2ecf20Sopenharmony_ci	reg = w83l786ng_read_value(client, W83L786NG_REG_FAN_CFG);
5468c2ecf20Sopenharmony_ci	data->pwm_enable[nr] = val;
5478c2ecf20Sopenharmony_ci	reg &= ~(0x03 << W83L786NG_PWM_ENABLE_SHIFT[nr]);
5488c2ecf20Sopenharmony_ci	reg |= (val - 1) << W83L786NG_PWM_ENABLE_SHIFT[nr];
5498c2ecf20Sopenharmony_ci	w83l786ng_write_value(client, W83L786NG_REG_FAN_CFG, reg);
5508c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
5518c2ecf20Sopenharmony_ci	return count;
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_cistatic struct sensor_device_attribute sda_pwm[] = {
5558c2ecf20Sopenharmony_ci	SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0),
5568c2ecf20Sopenharmony_ci	SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1),
5578c2ecf20Sopenharmony_ci};
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_cistatic struct sensor_device_attribute sda_pwm_mode[] = {
5608c2ecf20Sopenharmony_ci	SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
5618c2ecf20Sopenharmony_ci		    store_pwm_mode, 0),
5628c2ecf20Sopenharmony_ci	SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
5638c2ecf20Sopenharmony_ci		    store_pwm_mode, 1),
5648c2ecf20Sopenharmony_ci};
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_cistatic struct sensor_device_attribute sda_pwm_enable[] = {
5678c2ecf20Sopenharmony_ci	SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
5688c2ecf20Sopenharmony_ci		    store_pwm_enable, 0),
5698c2ecf20Sopenharmony_ci	SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
5708c2ecf20Sopenharmony_ci		    store_pwm_enable, 1),
5718c2ecf20Sopenharmony_ci};
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci/* For Smart Fan I/Thermal Cruise and Smart Fan II */
5748c2ecf20Sopenharmony_cistatic ssize_t
5758c2ecf20Sopenharmony_cishow_tolerance(struct device *dev, struct device_attribute *attr, char *buf)
5768c2ecf20Sopenharmony_ci{
5778c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(attr)->index;
5788c2ecf20Sopenharmony_ci	struct w83l786ng_data *data = w83l786ng_update_device(dev);
5798c2ecf20Sopenharmony_ci	return sprintf(buf, "%ld\n", (long)data->tolerance[nr]);
5808c2ecf20Sopenharmony_ci}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_cistatic ssize_t
5838c2ecf20Sopenharmony_cistore_tolerance(struct device *dev, struct device_attribute *attr,
5848c2ecf20Sopenharmony_ci		const char *buf, size_t count)
5858c2ecf20Sopenharmony_ci{
5868c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(attr)->index;
5878c2ecf20Sopenharmony_ci	struct w83l786ng_data *data = dev_get_drvdata(dev);
5888c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
5898c2ecf20Sopenharmony_ci	u8 tol_tmp, tol_mask;
5908c2ecf20Sopenharmony_ci	unsigned long val;
5918c2ecf20Sopenharmony_ci	int err;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	err = kstrtoul(buf, 10, &val);
5948c2ecf20Sopenharmony_ci	if (err)
5958c2ecf20Sopenharmony_ci		return err;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
5988c2ecf20Sopenharmony_ci	tol_mask = w83l786ng_read_value(client,
5998c2ecf20Sopenharmony_ci	    W83L786NG_REG_TOLERANCE) & ((nr == 1) ? 0x0f : 0xf0);
6008c2ecf20Sopenharmony_ci	tol_tmp = clamp_val(val, 0, 15);
6018c2ecf20Sopenharmony_ci	tol_tmp &= 0x0f;
6028c2ecf20Sopenharmony_ci	data->tolerance[nr] = tol_tmp;
6038c2ecf20Sopenharmony_ci	if (nr == 1)
6048c2ecf20Sopenharmony_ci		tol_tmp <<= 4;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	w83l786ng_write_value(client, W83L786NG_REG_TOLERANCE,
6078c2ecf20Sopenharmony_ci			      tol_mask | tol_tmp);
6088c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
6098c2ecf20Sopenharmony_ci	return count;
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_cistatic struct sensor_device_attribute sda_tolerance[] = {
6138c2ecf20Sopenharmony_ci	SENSOR_ATTR(pwm1_tolerance, S_IWUSR | S_IRUGO,
6148c2ecf20Sopenharmony_ci		    show_tolerance, store_tolerance, 0),
6158c2ecf20Sopenharmony_ci	SENSOR_ATTR(pwm2_tolerance, S_IWUSR | S_IRUGO,
6168c2ecf20Sopenharmony_ci		    show_tolerance, store_tolerance, 1),
6178c2ecf20Sopenharmony_ci};
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci#define IN_UNIT_ATTRS(X)	\
6218c2ecf20Sopenharmony_ci	&sda_in_input[X].dev_attr.attr,		\
6228c2ecf20Sopenharmony_ci	&sda_in_min[X].dev_attr.attr,		\
6238c2ecf20Sopenharmony_ci	&sda_in_max[X].dev_attr.attr
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci#define FAN_UNIT_ATTRS(X)	\
6268c2ecf20Sopenharmony_ci	&sda_fan_input[X].dev_attr.attr,	\
6278c2ecf20Sopenharmony_ci	&sda_fan_min[X].dev_attr.attr,		\
6288c2ecf20Sopenharmony_ci	&sda_fan_div[X].dev_attr.attr
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci#define TEMP_UNIT_ATTRS(X)	\
6318c2ecf20Sopenharmony_ci	&sda_temp_input[X].dev_attr.attr,	\
6328c2ecf20Sopenharmony_ci	&sda_temp_max[X].dev_attr.attr,		\
6338c2ecf20Sopenharmony_ci	&sda_temp_max_hyst[X].dev_attr.attr
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci#define PWM_UNIT_ATTRS(X)	\
6368c2ecf20Sopenharmony_ci	&sda_pwm[X].dev_attr.attr,		\
6378c2ecf20Sopenharmony_ci	&sda_pwm_mode[X].dev_attr.attr,		\
6388c2ecf20Sopenharmony_ci	&sda_pwm_enable[X].dev_attr.attr
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci#define TOLERANCE_UNIT_ATTRS(X)	\
6418c2ecf20Sopenharmony_ci	&sda_tolerance[X].dev_attr.attr
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_cistatic struct attribute *w83l786ng_attrs[] = {
6448c2ecf20Sopenharmony_ci	IN_UNIT_ATTRS(0),
6458c2ecf20Sopenharmony_ci	IN_UNIT_ATTRS(1),
6468c2ecf20Sopenharmony_ci	IN_UNIT_ATTRS(2),
6478c2ecf20Sopenharmony_ci	FAN_UNIT_ATTRS(0),
6488c2ecf20Sopenharmony_ci	FAN_UNIT_ATTRS(1),
6498c2ecf20Sopenharmony_ci	TEMP_UNIT_ATTRS(0),
6508c2ecf20Sopenharmony_ci	TEMP_UNIT_ATTRS(1),
6518c2ecf20Sopenharmony_ci	PWM_UNIT_ATTRS(0),
6528c2ecf20Sopenharmony_ci	PWM_UNIT_ATTRS(1),
6538c2ecf20Sopenharmony_ci	TOLERANCE_UNIT_ATTRS(0),
6548c2ecf20Sopenharmony_ci	TOLERANCE_UNIT_ATTRS(1),
6558c2ecf20Sopenharmony_ci	NULL
6568c2ecf20Sopenharmony_ci};
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(w83l786ng);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistatic int
6618c2ecf20Sopenharmony_ciw83l786ng_detect(struct i2c_client *client, struct i2c_board_info *info)
6628c2ecf20Sopenharmony_ci{
6638c2ecf20Sopenharmony_ci	struct i2c_adapter *adapter = client->adapter;
6648c2ecf20Sopenharmony_ci	u16 man_id;
6658c2ecf20Sopenharmony_ci	u8 chip_id;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
6688c2ecf20Sopenharmony_ci		return -ENODEV;
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	/* Detection */
6718c2ecf20Sopenharmony_ci	if ((w83l786ng_read_value(client, W83L786NG_REG_CONFIG) & 0x80)) {
6728c2ecf20Sopenharmony_ci		dev_dbg(&adapter->dev, "W83L786NG detection failed at 0x%02x\n",
6738c2ecf20Sopenharmony_ci			client->addr);
6748c2ecf20Sopenharmony_ci		return -ENODEV;
6758c2ecf20Sopenharmony_ci	}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	/* Identification */
6788c2ecf20Sopenharmony_ci	man_id = (w83l786ng_read_value(client, W83L786NG_REG_MAN_ID1) << 8) +
6798c2ecf20Sopenharmony_ci		 w83l786ng_read_value(client, W83L786NG_REG_MAN_ID2);
6808c2ecf20Sopenharmony_ci	chip_id = w83l786ng_read_value(client, W83L786NG_REG_CHIP_ID);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	if (man_id != 0x5CA3 ||		/* Winbond */
6838c2ecf20Sopenharmony_ci	    chip_id != 0x80) {		/* W83L786NG */
6848c2ecf20Sopenharmony_ci		dev_dbg(&adapter->dev,
6858c2ecf20Sopenharmony_ci			"Unsupported chip (man_id=0x%04X, chip_id=0x%02X)\n",
6868c2ecf20Sopenharmony_ci			man_id, chip_id);
6878c2ecf20Sopenharmony_ci		return -ENODEV;
6888c2ecf20Sopenharmony_ci	}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	strlcpy(info->type, "w83l786ng", I2C_NAME_SIZE);
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	return 0;
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_cistatic void w83l786ng_init_client(struct i2c_client *client)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci	u8 tmp;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	if (reset)
7008c2ecf20Sopenharmony_ci		w83l786ng_write_value(client, W83L786NG_REG_CONFIG, 0x80);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	/* Start monitoring */
7038c2ecf20Sopenharmony_ci	tmp = w83l786ng_read_value(client, W83L786NG_REG_CONFIG);
7048c2ecf20Sopenharmony_ci	if (!(tmp & 0x01))
7058c2ecf20Sopenharmony_ci		w83l786ng_write_value(client, W83L786NG_REG_CONFIG, tmp | 0x01);
7068c2ecf20Sopenharmony_ci}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_cistatic int
7098c2ecf20Sopenharmony_ciw83l786ng_probe(struct i2c_client *client)
7108c2ecf20Sopenharmony_ci{
7118c2ecf20Sopenharmony_ci	struct device *dev = &client->dev;
7128c2ecf20Sopenharmony_ci	struct w83l786ng_data *data;
7138c2ecf20Sopenharmony_ci	struct device *hwmon_dev;
7148c2ecf20Sopenharmony_ci	int i;
7158c2ecf20Sopenharmony_ci	u8 reg_tmp;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	data = devm_kzalloc(dev, sizeof(struct w83l786ng_data), GFP_KERNEL);
7188c2ecf20Sopenharmony_ci	if (!data)
7198c2ecf20Sopenharmony_ci		return -ENOMEM;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	data->client = client;
7228c2ecf20Sopenharmony_ci	mutex_init(&data->update_lock);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	/* Initialize the chip */
7258c2ecf20Sopenharmony_ci	w83l786ng_init_client(client);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	/* A few vars need to be filled upon startup */
7288c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
7298c2ecf20Sopenharmony_ci		data->fan_min[i] = w83l786ng_read_value(client,
7308c2ecf20Sopenharmony_ci		    W83L786NG_REG_FAN_MIN(i));
7318c2ecf20Sopenharmony_ci	}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	/* Update the fan divisor */
7348c2ecf20Sopenharmony_ci	reg_tmp = w83l786ng_read_value(client, W83L786NG_REG_FAN_DIV);
7358c2ecf20Sopenharmony_ci	data->fan_div[0] = reg_tmp & 0x07;
7368c2ecf20Sopenharmony_ci	data->fan_div[1] = (reg_tmp >> 4) & 0x07;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
7398c2ecf20Sopenharmony_ci							   data,
7408c2ecf20Sopenharmony_ci							   w83l786ng_groups);
7418c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(hwmon_dev);
7428c2ecf20Sopenharmony_ci}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_cistatic const struct i2c_device_id w83l786ng_id[] = {
7458c2ecf20Sopenharmony_ci	{ "w83l786ng", 0 },
7468c2ecf20Sopenharmony_ci	{ }
7478c2ecf20Sopenharmony_ci};
7488c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, w83l786ng_id);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_cistatic struct i2c_driver w83l786ng_driver = {
7518c2ecf20Sopenharmony_ci	.class		= I2C_CLASS_HWMON,
7528c2ecf20Sopenharmony_ci	.driver = {
7538c2ecf20Sopenharmony_ci		   .name = "w83l786ng",
7548c2ecf20Sopenharmony_ci	},
7558c2ecf20Sopenharmony_ci	.probe_new	= w83l786ng_probe,
7568c2ecf20Sopenharmony_ci	.id_table	= w83l786ng_id,
7578c2ecf20Sopenharmony_ci	.detect		= w83l786ng_detect,
7588c2ecf20Sopenharmony_ci	.address_list	= normal_i2c,
7598c2ecf20Sopenharmony_ci};
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_cimodule_i2c_driver(w83l786ng_driver);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kevin Lo");
7648c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("w83l786ng driver");
7658c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
766