18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * amc6821.c - Part of lm_sensors, Linux kernel modules for hardware
48c2ecf20Sopenharmony_ci *	       monitoring
58c2ecf20Sopenharmony_ci * Copyright (C) 2009 T. Mertelj <tomaz.mertelj@guest.arnes.si>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Based on max6650.c:
88c2ecf20Sopenharmony_ci * Copyright (C) 2007 Hans J. Koch <hjk@hansjkoch.de>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>	/* Needed for KERN_INFO */
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
168c2ecf20Sopenharmony_ci#include <linux/i2c.h>
178c2ecf20Sopenharmony_ci#include <linux/hwmon.h>
188c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h>
198c2ecf20Sopenharmony_ci#include <linux/err.h>
208c2ecf20Sopenharmony_ci#include <linux/mutex.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/*
238c2ecf20Sopenharmony_ci * Addresses to scan.
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic const unsigned short normal_i2c[] = {0x18, 0x19, 0x1a, 0x2c, 0x2d, 0x2e,
278c2ecf20Sopenharmony_ci	0x4c, 0x4d, 0x4e, I2C_CLIENT_END};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/*
308c2ecf20Sopenharmony_ci * Insmod parameters
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic int pwminv;	/*Inverted PWM output. */
348c2ecf20Sopenharmony_cimodule_param(pwminv, int, 0444);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int init = 1; /*Power-on initialization.*/
378c2ecf20Sopenharmony_cimodule_param(init, int, 0444);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cienum chips { amc6821 };
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define AMC6821_REG_DEV_ID 0x3D
428c2ecf20Sopenharmony_ci#define AMC6821_REG_COMP_ID 0x3E
438c2ecf20Sopenharmony_ci#define AMC6821_REG_CONF1 0x00
448c2ecf20Sopenharmony_ci#define AMC6821_REG_CONF2 0x01
458c2ecf20Sopenharmony_ci#define AMC6821_REG_CONF3 0x3F
468c2ecf20Sopenharmony_ci#define AMC6821_REG_CONF4 0x04
478c2ecf20Sopenharmony_ci#define AMC6821_REG_STAT1 0x02
488c2ecf20Sopenharmony_ci#define AMC6821_REG_STAT2 0x03
498c2ecf20Sopenharmony_ci#define AMC6821_REG_TDATA_LOW 0x08
508c2ecf20Sopenharmony_ci#define AMC6821_REG_TDATA_HI 0x09
518c2ecf20Sopenharmony_ci#define AMC6821_REG_LTEMP_HI 0x0A
528c2ecf20Sopenharmony_ci#define AMC6821_REG_RTEMP_HI 0x0B
538c2ecf20Sopenharmony_ci#define AMC6821_REG_LTEMP_LIMIT_MIN 0x15
548c2ecf20Sopenharmony_ci#define AMC6821_REG_LTEMP_LIMIT_MAX 0x14
558c2ecf20Sopenharmony_ci#define AMC6821_REG_RTEMP_LIMIT_MIN 0x19
568c2ecf20Sopenharmony_ci#define AMC6821_REG_RTEMP_LIMIT_MAX 0x18
578c2ecf20Sopenharmony_ci#define AMC6821_REG_LTEMP_CRIT 0x1B
588c2ecf20Sopenharmony_ci#define AMC6821_REG_RTEMP_CRIT 0x1D
598c2ecf20Sopenharmony_ci#define AMC6821_REG_PSV_TEMP 0x1C
608c2ecf20Sopenharmony_ci#define AMC6821_REG_DCY 0x22
618c2ecf20Sopenharmony_ci#define AMC6821_REG_LTEMP_FAN_CTRL 0x24
628c2ecf20Sopenharmony_ci#define AMC6821_REG_RTEMP_FAN_CTRL 0x25
638c2ecf20Sopenharmony_ci#define AMC6821_REG_DCY_LOW_TEMP 0x21
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define AMC6821_REG_TACH_LLIMITL 0x10
668c2ecf20Sopenharmony_ci#define AMC6821_REG_TACH_LLIMITH 0x11
678c2ecf20Sopenharmony_ci#define AMC6821_REG_TACH_HLIMITL 0x12
688c2ecf20Sopenharmony_ci#define AMC6821_REG_TACH_HLIMITH 0x13
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#define AMC6821_CONF1_START 0x01
718c2ecf20Sopenharmony_ci#define AMC6821_CONF1_FAN_INT_EN 0x02
728c2ecf20Sopenharmony_ci#define AMC6821_CONF1_FANIE 0x04
738c2ecf20Sopenharmony_ci#define AMC6821_CONF1_PWMINV 0x08
748c2ecf20Sopenharmony_ci#define AMC6821_CONF1_FAN_FAULT_EN 0x10
758c2ecf20Sopenharmony_ci#define AMC6821_CONF1_FDRC0 0x20
768c2ecf20Sopenharmony_ci#define AMC6821_CONF1_FDRC1 0x40
778c2ecf20Sopenharmony_ci#define AMC6821_CONF1_THERMOVIE 0x80
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci#define AMC6821_CONF2_PWM_EN 0x01
808c2ecf20Sopenharmony_ci#define AMC6821_CONF2_TACH_MODE 0x02
818c2ecf20Sopenharmony_ci#define AMC6821_CONF2_TACH_EN 0x04
828c2ecf20Sopenharmony_ci#define AMC6821_CONF2_RTFIE 0x08
838c2ecf20Sopenharmony_ci#define AMC6821_CONF2_LTOIE 0x10
848c2ecf20Sopenharmony_ci#define AMC6821_CONF2_RTOIE 0x20
858c2ecf20Sopenharmony_ci#define AMC6821_CONF2_PSVIE 0x40
868c2ecf20Sopenharmony_ci#define AMC6821_CONF2_RST 0x80
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci#define AMC6821_CONF3_THERM_FAN_EN 0x80
898c2ecf20Sopenharmony_ci#define AMC6821_CONF3_REV_MASK 0x0F
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci#define AMC6821_CONF4_OVREN 0x10
928c2ecf20Sopenharmony_ci#define AMC6821_CONF4_TACH_FAST 0x20
938c2ecf20Sopenharmony_ci#define AMC6821_CONF4_PSPR 0x40
948c2ecf20Sopenharmony_ci#define AMC6821_CONF4_MODE 0x80
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci#define AMC6821_STAT1_RPM_ALARM 0x01
978c2ecf20Sopenharmony_ci#define AMC6821_STAT1_FANS 0x02
988c2ecf20Sopenharmony_ci#define AMC6821_STAT1_RTH 0x04
998c2ecf20Sopenharmony_ci#define AMC6821_STAT1_RTL 0x08
1008c2ecf20Sopenharmony_ci#define AMC6821_STAT1_R_THERM 0x10
1018c2ecf20Sopenharmony_ci#define AMC6821_STAT1_RTF 0x20
1028c2ecf20Sopenharmony_ci#define AMC6821_STAT1_LTH 0x40
1038c2ecf20Sopenharmony_ci#define AMC6821_STAT1_LTL 0x80
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci#define AMC6821_STAT2_RTC 0x08
1068c2ecf20Sopenharmony_ci#define AMC6821_STAT2_LTC 0x10
1078c2ecf20Sopenharmony_ci#define AMC6821_STAT2_LPSV 0x20
1088c2ecf20Sopenharmony_ci#define AMC6821_STAT2_L_THERM 0x40
1098c2ecf20Sopenharmony_ci#define AMC6821_STAT2_THERM_IN 0x80
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cienum {IDX_TEMP1_INPUT = 0, IDX_TEMP1_MIN, IDX_TEMP1_MAX,
1128c2ecf20Sopenharmony_ci	IDX_TEMP1_CRIT, IDX_TEMP2_INPUT, IDX_TEMP2_MIN,
1138c2ecf20Sopenharmony_ci	IDX_TEMP2_MAX, IDX_TEMP2_CRIT,
1148c2ecf20Sopenharmony_ci	TEMP_IDX_LEN, };
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic const u8 temp_reg[] = {AMC6821_REG_LTEMP_HI,
1178c2ecf20Sopenharmony_ci			AMC6821_REG_LTEMP_LIMIT_MIN,
1188c2ecf20Sopenharmony_ci			AMC6821_REG_LTEMP_LIMIT_MAX,
1198c2ecf20Sopenharmony_ci			AMC6821_REG_LTEMP_CRIT,
1208c2ecf20Sopenharmony_ci			AMC6821_REG_RTEMP_HI,
1218c2ecf20Sopenharmony_ci			AMC6821_REG_RTEMP_LIMIT_MIN,
1228c2ecf20Sopenharmony_ci			AMC6821_REG_RTEMP_LIMIT_MAX,
1238c2ecf20Sopenharmony_ci			AMC6821_REG_RTEMP_CRIT, };
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cienum {IDX_FAN1_INPUT = 0, IDX_FAN1_MIN, IDX_FAN1_MAX,
1268c2ecf20Sopenharmony_ci	FAN1_IDX_LEN, };
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic const u8 fan_reg_low[] = {AMC6821_REG_TDATA_LOW,
1298c2ecf20Sopenharmony_ci			AMC6821_REG_TACH_LLIMITL,
1308c2ecf20Sopenharmony_ci			AMC6821_REG_TACH_HLIMITL, };
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic const u8 fan_reg_hi[] = {AMC6821_REG_TDATA_HI,
1348c2ecf20Sopenharmony_ci			AMC6821_REG_TACH_LLIMITH,
1358c2ecf20Sopenharmony_ci			AMC6821_REG_TACH_HLIMITH, };
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci/*
1388c2ecf20Sopenharmony_ci * Client data (each client gets its own)
1398c2ecf20Sopenharmony_ci */
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistruct amc6821_data {
1428c2ecf20Sopenharmony_ci	struct i2c_client *client;
1438c2ecf20Sopenharmony_ci	struct mutex update_lock;
1448c2ecf20Sopenharmony_ci	char valid; /* zero until following fields are valid */
1458c2ecf20Sopenharmony_ci	unsigned long last_updated; /* in jiffies */
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	/* register values */
1488c2ecf20Sopenharmony_ci	int temp[TEMP_IDX_LEN];
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	u16 fan[FAN1_IDX_LEN];
1518c2ecf20Sopenharmony_ci	u8 fan1_div;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	u8 pwm1;
1548c2ecf20Sopenharmony_ci	u8 temp1_auto_point_temp[3];
1558c2ecf20Sopenharmony_ci	u8 temp2_auto_point_temp[3];
1568c2ecf20Sopenharmony_ci	u8 pwm1_auto_point_pwm[3];
1578c2ecf20Sopenharmony_ci	u8 pwm1_enable;
1588c2ecf20Sopenharmony_ci	u8 pwm1_auto_channels_temp;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	u8 stat1;
1618c2ecf20Sopenharmony_ci	u8 stat2;
1628c2ecf20Sopenharmony_ci};
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic struct amc6821_data *amc6821_update_device(struct device *dev)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct amc6821_data *data = dev_get_drvdata(dev);
1678c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
1688c2ecf20Sopenharmony_ci	int timeout = HZ;
1698c2ecf20Sopenharmony_ci	u8 reg;
1708c2ecf20Sopenharmony_ci	int i;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (time_after(jiffies, data->last_updated + timeout) ||
1758c2ecf20Sopenharmony_ci			!data->valid) {
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		for (i = 0; i < TEMP_IDX_LEN; i++)
1788c2ecf20Sopenharmony_ci			data->temp[i] = (int8_t)i2c_smbus_read_byte_data(
1798c2ecf20Sopenharmony_ci				client, temp_reg[i]);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci		data->stat1 = i2c_smbus_read_byte_data(client,
1828c2ecf20Sopenharmony_ci			AMC6821_REG_STAT1);
1838c2ecf20Sopenharmony_ci		data->stat2 = i2c_smbus_read_byte_data(client,
1848c2ecf20Sopenharmony_ci			AMC6821_REG_STAT2);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		data->pwm1 = i2c_smbus_read_byte_data(client,
1878c2ecf20Sopenharmony_ci			AMC6821_REG_DCY);
1888c2ecf20Sopenharmony_ci		for (i = 0; i < FAN1_IDX_LEN; i++) {
1898c2ecf20Sopenharmony_ci			data->fan[i] = i2c_smbus_read_byte_data(
1908c2ecf20Sopenharmony_ci					client,
1918c2ecf20Sopenharmony_ci					fan_reg_low[i]);
1928c2ecf20Sopenharmony_ci			data->fan[i] += i2c_smbus_read_byte_data(
1938c2ecf20Sopenharmony_ci					client,
1948c2ecf20Sopenharmony_ci					fan_reg_hi[i]) << 8;
1958c2ecf20Sopenharmony_ci		}
1968c2ecf20Sopenharmony_ci		data->fan1_div = i2c_smbus_read_byte_data(client,
1978c2ecf20Sopenharmony_ci			AMC6821_REG_CONF4);
1988c2ecf20Sopenharmony_ci		data->fan1_div = data->fan1_div & AMC6821_CONF4_PSPR ? 4 : 2;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci		data->pwm1_auto_point_pwm[0] = 0;
2018c2ecf20Sopenharmony_ci		data->pwm1_auto_point_pwm[2] = 255;
2028c2ecf20Sopenharmony_ci		data->pwm1_auto_point_pwm[1] = i2c_smbus_read_byte_data(client,
2038c2ecf20Sopenharmony_ci			AMC6821_REG_DCY_LOW_TEMP);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci		data->temp1_auto_point_temp[0] =
2068c2ecf20Sopenharmony_ci			i2c_smbus_read_byte_data(client,
2078c2ecf20Sopenharmony_ci					AMC6821_REG_PSV_TEMP);
2088c2ecf20Sopenharmony_ci		data->temp2_auto_point_temp[0] =
2098c2ecf20Sopenharmony_ci				data->temp1_auto_point_temp[0];
2108c2ecf20Sopenharmony_ci		reg = i2c_smbus_read_byte_data(client,
2118c2ecf20Sopenharmony_ci			AMC6821_REG_LTEMP_FAN_CTRL);
2128c2ecf20Sopenharmony_ci		data->temp1_auto_point_temp[1] = (reg & 0xF8) >> 1;
2138c2ecf20Sopenharmony_ci		reg &= 0x07;
2148c2ecf20Sopenharmony_ci		reg = 0x20 >> reg;
2158c2ecf20Sopenharmony_ci		if (reg > 0)
2168c2ecf20Sopenharmony_ci			data->temp1_auto_point_temp[2] =
2178c2ecf20Sopenharmony_ci				data->temp1_auto_point_temp[1] +
2188c2ecf20Sopenharmony_ci				(data->pwm1_auto_point_pwm[2] -
2198c2ecf20Sopenharmony_ci				data->pwm1_auto_point_pwm[1]) / reg;
2208c2ecf20Sopenharmony_ci		else
2218c2ecf20Sopenharmony_ci			data->temp1_auto_point_temp[2] = 255;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci		reg = i2c_smbus_read_byte_data(client,
2248c2ecf20Sopenharmony_ci			AMC6821_REG_RTEMP_FAN_CTRL);
2258c2ecf20Sopenharmony_ci		data->temp2_auto_point_temp[1] = (reg & 0xF8) >> 1;
2268c2ecf20Sopenharmony_ci		reg &= 0x07;
2278c2ecf20Sopenharmony_ci		reg = 0x20 >> reg;
2288c2ecf20Sopenharmony_ci		if (reg > 0)
2298c2ecf20Sopenharmony_ci			data->temp2_auto_point_temp[2] =
2308c2ecf20Sopenharmony_ci				data->temp2_auto_point_temp[1] +
2318c2ecf20Sopenharmony_ci				(data->pwm1_auto_point_pwm[2] -
2328c2ecf20Sopenharmony_ci				data->pwm1_auto_point_pwm[1]) / reg;
2338c2ecf20Sopenharmony_ci		else
2348c2ecf20Sopenharmony_ci			data->temp2_auto_point_temp[2] = 255;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci		reg = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1);
2378c2ecf20Sopenharmony_ci		reg = (reg >> 5) & 0x3;
2388c2ecf20Sopenharmony_ci		switch (reg) {
2398c2ecf20Sopenharmony_ci		case 0: /*open loop: software sets pwm1*/
2408c2ecf20Sopenharmony_ci			data->pwm1_auto_channels_temp = 0;
2418c2ecf20Sopenharmony_ci			data->pwm1_enable = 1;
2428c2ecf20Sopenharmony_ci			break;
2438c2ecf20Sopenharmony_ci		case 2: /*closed loop: remote T (temp2)*/
2448c2ecf20Sopenharmony_ci			data->pwm1_auto_channels_temp = 2;
2458c2ecf20Sopenharmony_ci			data->pwm1_enable = 2;
2468c2ecf20Sopenharmony_ci			break;
2478c2ecf20Sopenharmony_ci		case 3: /*closed loop: local and remote T (temp2)*/
2488c2ecf20Sopenharmony_ci			data->pwm1_auto_channels_temp = 3;
2498c2ecf20Sopenharmony_ci			data->pwm1_enable = 3;
2508c2ecf20Sopenharmony_ci			break;
2518c2ecf20Sopenharmony_ci		case 1: /*
2528c2ecf20Sopenharmony_ci			 * semi-open loop: software sets rpm, chip controls
2538c2ecf20Sopenharmony_ci			 * pwm1, currently not implemented
2548c2ecf20Sopenharmony_ci			 */
2558c2ecf20Sopenharmony_ci			data->pwm1_auto_channels_temp = 0;
2568c2ecf20Sopenharmony_ci			data->pwm1_enable = 0;
2578c2ecf20Sopenharmony_ci			break;
2588c2ecf20Sopenharmony_ci		}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		data->last_updated = jiffies;
2618c2ecf20Sopenharmony_ci		data->valid = 1;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
2648c2ecf20Sopenharmony_ci	return data;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
2688c2ecf20Sopenharmony_ci			 char *buf)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct amc6821_data *data = amc6821_update_device(dev);
2718c2ecf20Sopenharmony_ci	int ix = to_sensor_dev_attr(devattr)->index;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", data->temp[ix] * 1000);
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic ssize_t temp_store(struct device *dev, struct device_attribute *attr,
2778c2ecf20Sopenharmony_ci			  const char *buf, size_t count)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	struct amc6821_data *data = dev_get_drvdata(dev);
2808c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
2818c2ecf20Sopenharmony_ci	int ix = to_sensor_dev_attr(attr)->index;
2828c2ecf20Sopenharmony_ci	long val;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	int ret = kstrtol(buf, 10, &val);
2858c2ecf20Sopenharmony_ci	if (ret)
2868c2ecf20Sopenharmony_ci		return ret;
2878c2ecf20Sopenharmony_ci	val = clamp_val(val / 1000, -128, 127);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
2908c2ecf20Sopenharmony_ci	data->temp[ix] = val;
2918c2ecf20Sopenharmony_ci	if (i2c_smbus_write_byte_data(client, temp_reg[ix], data->temp[ix])) {
2928c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Register write error, aborting.\n");
2938c2ecf20Sopenharmony_ci		count = -EIO;
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
2968c2ecf20Sopenharmony_ci	return count;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic ssize_t temp_alarm_show(struct device *dev,
3008c2ecf20Sopenharmony_ci			       struct device_attribute *devattr, char *buf)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct amc6821_data *data = amc6821_update_device(dev);
3038c2ecf20Sopenharmony_ci	int ix = to_sensor_dev_attr(devattr)->index;
3048c2ecf20Sopenharmony_ci	u8 flag;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	switch (ix) {
3078c2ecf20Sopenharmony_ci	case IDX_TEMP1_MIN:
3088c2ecf20Sopenharmony_ci		flag = data->stat1 & AMC6821_STAT1_LTL;
3098c2ecf20Sopenharmony_ci		break;
3108c2ecf20Sopenharmony_ci	case IDX_TEMP1_MAX:
3118c2ecf20Sopenharmony_ci		flag = data->stat1 & AMC6821_STAT1_LTH;
3128c2ecf20Sopenharmony_ci		break;
3138c2ecf20Sopenharmony_ci	case IDX_TEMP1_CRIT:
3148c2ecf20Sopenharmony_ci		flag = data->stat2 & AMC6821_STAT2_LTC;
3158c2ecf20Sopenharmony_ci		break;
3168c2ecf20Sopenharmony_ci	case IDX_TEMP2_MIN:
3178c2ecf20Sopenharmony_ci		flag = data->stat1 & AMC6821_STAT1_RTL;
3188c2ecf20Sopenharmony_ci		break;
3198c2ecf20Sopenharmony_ci	case IDX_TEMP2_MAX:
3208c2ecf20Sopenharmony_ci		flag = data->stat1 & AMC6821_STAT1_RTH;
3218c2ecf20Sopenharmony_ci		break;
3228c2ecf20Sopenharmony_ci	case IDX_TEMP2_CRIT:
3238c2ecf20Sopenharmony_ci		flag = data->stat2 & AMC6821_STAT2_RTC;
3248c2ecf20Sopenharmony_ci		break;
3258c2ecf20Sopenharmony_ci	default:
3268c2ecf20Sopenharmony_ci		dev_dbg(dev, "Unknown attr->index (%d).\n", ix);
3278c2ecf20Sopenharmony_ci		return -EINVAL;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci	if (flag)
3308c2ecf20Sopenharmony_ci		return sprintf(buf, "1");
3318c2ecf20Sopenharmony_ci	else
3328c2ecf20Sopenharmony_ci		return sprintf(buf, "0");
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic ssize_t temp2_fault_show(struct device *dev,
3368c2ecf20Sopenharmony_ci				struct device_attribute *devattr, char *buf)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct amc6821_data *data = amc6821_update_device(dev);
3398c2ecf20Sopenharmony_ci	if (data->stat1 & AMC6821_STAT1_RTF)
3408c2ecf20Sopenharmony_ci		return sprintf(buf, "1");
3418c2ecf20Sopenharmony_ci	else
3428c2ecf20Sopenharmony_ci		return sprintf(buf, "0");
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cistatic ssize_t pwm1_show(struct device *dev, struct device_attribute *devattr,
3468c2ecf20Sopenharmony_ci			 char *buf)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	struct amc6821_data *data = amc6821_update_device(dev);
3498c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", data->pwm1);
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic ssize_t pwm1_store(struct device *dev,
3538c2ecf20Sopenharmony_ci			  struct device_attribute *devattr, const char *buf,
3548c2ecf20Sopenharmony_ci			  size_t count)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	struct amc6821_data *data = dev_get_drvdata(dev);
3578c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
3588c2ecf20Sopenharmony_ci	long val;
3598c2ecf20Sopenharmony_ci	int ret = kstrtol(buf, 10, &val);
3608c2ecf20Sopenharmony_ci	if (ret)
3618c2ecf20Sopenharmony_ci		return ret;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
3648c2ecf20Sopenharmony_ci	data->pwm1 = clamp_val(val , 0, 255);
3658c2ecf20Sopenharmony_ci	i2c_smbus_write_byte_data(client, AMC6821_REG_DCY, data->pwm1);
3668c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
3678c2ecf20Sopenharmony_ci	return count;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic ssize_t pwm1_enable_show(struct device *dev,
3718c2ecf20Sopenharmony_ci				struct device_attribute *devattr, char *buf)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct amc6821_data *data = amc6821_update_device(dev);
3748c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", data->pwm1_enable);
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic ssize_t pwm1_enable_store(struct device *dev,
3788c2ecf20Sopenharmony_ci				 struct device_attribute *attr,
3798c2ecf20Sopenharmony_ci				 const char *buf, size_t count)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	struct amc6821_data *data = dev_get_drvdata(dev);
3828c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
3838c2ecf20Sopenharmony_ci	long val;
3848c2ecf20Sopenharmony_ci	int config = kstrtol(buf, 10, &val);
3858c2ecf20Sopenharmony_ci	if (config)
3868c2ecf20Sopenharmony_ci		return config;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
3898c2ecf20Sopenharmony_ci	config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1);
3908c2ecf20Sopenharmony_ci	if (config < 0) {
3918c2ecf20Sopenharmony_ci			dev_err(&client->dev,
3928c2ecf20Sopenharmony_ci			"Error reading configuration register, aborting.\n");
3938c2ecf20Sopenharmony_ci			count = config;
3948c2ecf20Sopenharmony_ci			goto unlock;
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	switch (val) {
3988c2ecf20Sopenharmony_ci	case 1:
3998c2ecf20Sopenharmony_ci		config &= ~AMC6821_CONF1_FDRC0;
4008c2ecf20Sopenharmony_ci		config &= ~AMC6821_CONF1_FDRC1;
4018c2ecf20Sopenharmony_ci		break;
4028c2ecf20Sopenharmony_ci	case 2:
4038c2ecf20Sopenharmony_ci		config &= ~AMC6821_CONF1_FDRC0;
4048c2ecf20Sopenharmony_ci		config |= AMC6821_CONF1_FDRC1;
4058c2ecf20Sopenharmony_ci		break;
4068c2ecf20Sopenharmony_ci	case 3:
4078c2ecf20Sopenharmony_ci		config |= AMC6821_CONF1_FDRC0;
4088c2ecf20Sopenharmony_ci		config |= AMC6821_CONF1_FDRC1;
4098c2ecf20Sopenharmony_ci		break;
4108c2ecf20Sopenharmony_ci	default:
4118c2ecf20Sopenharmony_ci		count = -EINVAL;
4128c2ecf20Sopenharmony_ci		goto unlock;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci	if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF1, config)) {
4158c2ecf20Sopenharmony_ci			dev_err(&client->dev,
4168c2ecf20Sopenharmony_ci			"Configuration register write error, aborting.\n");
4178c2ecf20Sopenharmony_ci			count = -EIO;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ciunlock:
4208c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
4218c2ecf20Sopenharmony_ci	return count;
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic ssize_t pwm1_auto_channels_temp_show(struct device *dev,
4258c2ecf20Sopenharmony_ci					    struct device_attribute *devattr,
4268c2ecf20Sopenharmony_ci					    char *buf)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	struct amc6821_data *data = amc6821_update_device(dev);
4298c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", data->pwm1_auto_channels_temp);
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_cistatic ssize_t temp_auto_point_temp_show(struct device *dev,
4338c2ecf20Sopenharmony_ci					 struct device_attribute *devattr,
4348c2ecf20Sopenharmony_ci					 char *buf)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	int ix = to_sensor_dev_attr_2(devattr)->index;
4378c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr_2(devattr)->nr;
4388c2ecf20Sopenharmony_ci	struct amc6821_data *data = amc6821_update_device(dev);
4398c2ecf20Sopenharmony_ci	switch (nr) {
4408c2ecf20Sopenharmony_ci	case 1:
4418c2ecf20Sopenharmony_ci		return sprintf(buf, "%d\n",
4428c2ecf20Sopenharmony_ci			data->temp1_auto_point_temp[ix] * 1000);
4438c2ecf20Sopenharmony_ci	case 2:
4448c2ecf20Sopenharmony_ci		return sprintf(buf, "%d\n",
4458c2ecf20Sopenharmony_ci			data->temp2_auto_point_temp[ix] * 1000);
4468c2ecf20Sopenharmony_ci	default:
4478c2ecf20Sopenharmony_ci		dev_dbg(dev, "Unknown attr->nr (%d).\n", nr);
4488c2ecf20Sopenharmony_ci		return -EINVAL;
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic ssize_t pwm1_auto_point_pwm_show(struct device *dev,
4538c2ecf20Sopenharmony_ci					struct device_attribute *devattr,
4548c2ecf20Sopenharmony_ci					char *buf)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	int ix = to_sensor_dev_attr(devattr)->index;
4578c2ecf20Sopenharmony_ci	struct amc6821_data *data = amc6821_update_device(dev);
4588c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", data->pwm1_auto_point_pwm[ix]);
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic inline ssize_t set_slope_register(struct i2c_client *client,
4628c2ecf20Sopenharmony_ci		u8 reg,
4638c2ecf20Sopenharmony_ci		u8 dpwm,
4648c2ecf20Sopenharmony_ci		u8 *ptemp)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	int dt;
4678c2ecf20Sopenharmony_ci	u8 tmp;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	dt = ptemp[2]-ptemp[1];
4708c2ecf20Sopenharmony_ci	for (tmp = 4; tmp > 0; tmp--) {
4718c2ecf20Sopenharmony_ci		if (dt * (0x20 >> tmp) >= dpwm)
4728c2ecf20Sopenharmony_ci			break;
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci	tmp |= (ptemp[1] & 0x7C) << 1;
4758c2ecf20Sopenharmony_ci	if (i2c_smbus_write_byte_data(client,
4768c2ecf20Sopenharmony_ci			reg, tmp)) {
4778c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Register write error, aborting.\n");
4788c2ecf20Sopenharmony_ci		return -EIO;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci	return 0;
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic ssize_t temp_auto_point_temp_store(struct device *dev,
4848c2ecf20Sopenharmony_ci					  struct device_attribute *attr,
4858c2ecf20Sopenharmony_ci					  const char *buf, size_t count)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	struct amc6821_data *data = amc6821_update_device(dev);
4888c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
4898c2ecf20Sopenharmony_ci	int ix = to_sensor_dev_attr_2(attr)->index;
4908c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr_2(attr)->nr;
4918c2ecf20Sopenharmony_ci	u8 *ptemp;
4928c2ecf20Sopenharmony_ci	u8 reg;
4938c2ecf20Sopenharmony_ci	int dpwm;
4948c2ecf20Sopenharmony_ci	long val;
4958c2ecf20Sopenharmony_ci	int ret = kstrtol(buf, 10, &val);
4968c2ecf20Sopenharmony_ci	if (ret)
4978c2ecf20Sopenharmony_ci		return ret;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	switch (nr) {
5008c2ecf20Sopenharmony_ci	case 1:
5018c2ecf20Sopenharmony_ci		ptemp = data->temp1_auto_point_temp;
5028c2ecf20Sopenharmony_ci		reg = AMC6821_REG_LTEMP_FAN_CTRL;
5038c2ecf20Sopenharmony_ci		break;
5048c2ecf20Sopenharmony_ci	case 2:
5058c2ecf20Sopenharmony_ci		ptemp = data->temp2_auto_point_temp;
5068c2ecf20Sopenharmony_ci		reg = AMC6821_REG_RTEMP_FAN_CTRL;
5078c2ecf20Sopenharmony_ci		break;
5088c2ecf20Sopenharmony_ci	default:
5098c2ecf20Sopenharmony_ci		dev_dbg(dev, "Unknown attr->nr (%d).\n", nr);
5108c2ecf20Sopenharmony_ci		return -EINVAL;
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
5148c2ecf20Sopenharmony_ci	data->valid = 0;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	switch (ix) {
5178c2ecf20Sopenharmony_ci	case 0:
5188c2ecf20Sopenharmony_ci		ptemp[0] = clamp_val(val / 1000, 0,
5198c2ecf20Sopenharmony_ci				     data->temp1_auto_point_temp[1]);
5208c2ecf20Sopenharmony_ci		ptemp[0] = clamp_val(ptemp[0], 0,
5218c2ecf20Sopenharmony_ci				     data->temp2_auto_point_temp[1]);
5228c2ecf20Sopenharmony_ci		ptemp[0] = clamp_val(ptemp[0], 0, 63);
5238c2ecf20Sopenharmony_ci		if (i2c_smbus_write_byte_data(
5248c2ecf20Sopenharmony_ci					client,
5258c2ecf20Sopenharmony_ci					AMC6821_REG_PSV_TEMP,
5268c2ecf20Sopenharmony_ci					ptemp[0])) {
5278c2ecf20Sopenharmony_ci				dev_err(&client->dev,
5288c2ecf20Sopenharmony_ci					"Register write error, aborting.\n");
5298c2ecf20Sopenharmony_ci				count = -EIO;
5308c2ecf20Sopenharmony_ci		}
5318c2ecf20Sopenharmony_ci		goto EXIT;
5328c2ecf20Sopenharmony_ci	case 1:
5338c2ecf20Sopenharmony_ci		ptemp[1] = clamp_val(val / 1000, (ptemp[0] & 0x7C) + 4, 124);
5348c2ecf20Sopenharmony_ci		ptemp[1] &= 0x7C;
5358c2ecf20Sopenharmony_ci		ptemp[2] = clamp_val(ptemp[2], ptemp[1] + 1, 255);
5368c2ecf20Sopenharmony_ci		break;
5378c2ecf20Sopenharmony_ci	case 2:
5388c2ecf20Sopenharmony_ci		ptemp[2] = clamp_val(val / 1000, ptemp[1]+1, 255);
5398c2ecf20Sopenharmony_ci		break;
5408c2ecf20Sopenharmony_ci	default:
5418c2ecf20Sopenharmony_ci		dev_dbg(dev, "Unknown attr->index (%d).\n", ix);
5428c2ecf20Sopenharmony_ci		count = -EINVAL;
5438c2ecf20Sopenharmony_ci		goto EXIT;
5448c2ecf20Sopenharmony_ci	}
5458c2ecf20Sopenharmony_ci	dpwm = data->pwm1_auto_point_pwm[2] - data->pwm1_auto_point_pwm[1];
5468c2ecf20Sopenharmony_ci	if (set_slope_register(client, reg, dpwm, ptemp))
5478c2ecf20Sopenharmony_ci		count = -EIO;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ciEXIT:
5508c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
5518c2ecf20Sopenharmony_ci	return count;
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_cistatic ssize_t pwm1_auto_point_pwm_store(struct device *dev,
5558c2ecf20Sopenharmony_ci					 struct device_attribute *attr,
5568c2ecf20Sopenharmony_ci					 const char *buf, size_t count)
5578c2ecf20Sopenharmony_ci{
5588c2ecf20Sopenharmony_ci	struct amc6821_data *data = dev_get_drvdata(dev);
5598c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
5608c2ecf20Sopenharmony_ci	int dpwm;
5618c2ecf20Sopenharmony_ci	long val;
5628c2ecf20Sopenharmony_ci	int ret = kstrtol(buf, 10, &val);
5638c2ecf20Sopenharmony_ci	if (ret)
5648c2ecf20Sopenharmony_ci		return ret;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
5678c2ecf20Sopenharmony_ci	data->pwm1_auto_point_pwm[1] = clamp_val(val, 0, 254);
5688c2ecf20Sopenharmony_ci	if (i2c_smbus_write_byte_data(client, AMC6821_REG_DCY_LOW_TEMP,
5698c2ecf20Sopenharmony_ci			data->pwm1_auto_point_pwm[1])) {
5708c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Register write error, aborting.\n");
5718c2ecf20Sopenharmony_ci		count = -EIO;
5728c2ecf20Sopenharmony_ci		goto EXIT;
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci	dpwm = data->pwm1_auto_point_pwm[2] - data->pwm1_auto_point_pwm[1];
5758c2ecf20Sopenharmony_ci	if (set_slope_register(client, AMC6821_REG_LTEMP_FAN_CTRL, dpwm,
5768c2ecf20Sopenharmony_ci			data->temp1_auto_point_temp)) {
5778c2ecf20Sopenharmony_ci		count = -EIO;
5788c2ecf20Sopenharmony_ci		goto EXIT;
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci	if (set_slope_register(client, AMC6821_REG_RTEMP_FAN_CTRL, dpwm,
5818c2ecf20Sopenharmony_ci			data->temp2_auto_point_temp)) {
5828c2ecf20Sopenharmony_ci		count = -EIO;
5838c2ecf20Sopenharmony_ci		goto EXIT;
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ciEXIT:
5878c2ecf20Sopenharmony_ci	data->valid = 0;
5888c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
5898c2ecf20Sopenharmony_ci	return count;
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_cistatic ssize_t fan_show(struct device *dev, struct device_attribute *devattr,
5938c2ecf20Sopenharmony_ci			char *buf)
5948c2ecf20Sopenharmony_ci{
5958c2ecf20Sopenharmony_ci	struct amc6821_data *data = amc6821_update_device(dev);
5968c2ecf20Sopenharmony_ci	int ix = to_sensor_dev_attr(devattr)->index;
5978c2ecf20Sopenharmony_ci	if (0 == data->fan[ix])
5988c2ecf20Sopenharmony_ci		return sprintf(buf, "0");
5998c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", (int)(6000000 / data->fan[ix]));
6008c2ecf20Sopenharmony_ci}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic ssize_t fan1_fault_show(struct device *dev,
6038c2ecf20Sopenharmony_ci			       struct device_attribute *devattr, char *buf)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci	struct amc6821_data *data = amc6821_update_device(dev);
6068c2ecf20Sopenharmony_ci	if (data->stat1 & AMC6821_STAT1_FANS)
6078c2ecf20Sopenharmony_ci		return sprintf(buf, "1");
6088c2ecf20Sopenharmony_ci	else
6098c2ecf20Sopenharmony_ci		return sprintf(buf, "0");
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_cistatic ssize_t fan_store(struct device *dev, struct device_attribute *attr,
6138c2ecf20Sopenharmony_ci			 const char *buf, size_t count)
6148c2ecf20Sopenharmony_ci{
6158c2ecf20Sopenharmony_ci	struct amc6821_data *data = dev_get_drvdata(dev);
6168c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
6178c2ecf20Sopenharmony_ci	long val;
6188c2ecf20Sopenharmony_ci	int ix = to_sensor_dev_attr(attr)->index;
6198c2ecf20Sopenharmony_ci	int ret = kstrtol(buf, 10, &val);
6208c2ecf20Sopenharmony_ci	if (ret)
6218c2ecf20Sopenharmony_ci		return ret;
6228c2ecf20Sopenharmony_ci	val = 1 > val ? 0xFFFF : 6000000/val;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
6258c2ecf20Sopenharmony_ci	data->fan[ix] = (u16) clamp_val(val, 1, 0xFFFF);
6268c2ecf20Sopenharmony_ci	if (i2c_smbus_write_byte_data(client, fan_reg_low[ix],
6278c2ecf20Sopenharmony_ci			data->fan[ix] & 0xFF)) {
6288c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Register write error, aborting.\n");
6298c2ecf20Sopenharmony_ci		count = -EIO;
6308c2ecf20Sopenharmony_ci		goto EXIT;
6318c2ecf20Sopenharmony_ci	}
6328c2ecf20Sopenharmony_ci	if (i2c_smbus_write_byte_data(client,
6338c2ecf20Sopenharmony_ci			fan_reg_hi[ix], data->fan[ix] >> 8)) {
6348c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Register write error, aborting.\n");
6358c2ecf20Sopenharmony_ci		count = -EIO;
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ciEXIT:
6388c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
6398c2ecf20Sopenharmony_ci	return count;
6408c2ecf20Sopenharmony_ci}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_cistatic ssize_t fan1_div_show(struct device *dev,
6438c2ecf20Sopenharmony_ci			     struct device_attribute *devattr, char *buf)
6448c2ecf20Sopenharmony_ci{
6458c2ecf20Sopenharmony_ci	struct amc6821_data *data = amc6821_update_device(dev);
6468c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", data->fan1_div);
6478c2ecf20Sopenharmony_ci}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_cistatic ssize_t fan1_div_store(struct device *dev,
6508c2ecf20Sopenharmony_ci			      struct device_attribute *attr, const char *buf,
6518c2ecf20Sopenharmony_ci			      size_t count)
6528c2ecf20Sopenharmony_ci{
6538c2ecf20Sopenharmony_ci	struct amc6821_data *data = dev_get_drvdata(dev);
6548c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
6558c2ecf20Sopenharmony_ci	long val;
6568c2ecf20Sopenharmony_ci	int config = kstrtol(buf, 10, &val);
6578c2ecf20Sopenharmony_ci	if (config)
6588c2ecf20Sopenharmony_ci		return config;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
6618c2ecf20Sopenharmony_ci	config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4);
6628c2ecf20Sopenharmony_ci	if (config < 0) {
6638c2ecf20Sopenharmony_ci		dev_err(&client->dev,
6648c2ecf20Sopenharmony_ci			"Error reading configuration register, aborting.\n");
6658c2ecf20Sopenharmony_ci		count = config;
6668c2ecf20Sopenharmony_ci		goto EXIT;
6678c2ecf20Sopenharmony_ci	}
6688c2ecf20Sopenharmony_ci	switch (val) {
6698c2ecf20Sopenharmony_ci	case 2:
6708c2ecf20Sopenharmony_ci		config &= ~AMC6821_CONF4_PSPR;
6718c2ecf20Sopenharmony_ci		data->fan1_div = 2;
6728c2ecf20Sopenharmony_ci		break;
6738c2ecf20Sopenharmony_ci	case 4:
6748c2ecf20Sopenharmony_ci		config |= AMC6821_CONF4_PSPR;
6758c2ecf20Sopenharmony_ci		data->fan1_div = 4;
6768c2ecf20Sopenharmony_ci		break;
6778c2ecf20Sopenharmony_ci	default:
6788c2ecf20Sopenharmony_ci		count = -EINVAL;
6798c2ecf20Sopenharmony_ci		goto EXIT;
6808c2ecf20Sopenharmony_ci	}
6818c2ecf20Sopenharmony_ci	if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF4, config)) {
6828c2ecf20Sopenharmony_ci		dev_err(&client->dev,
6838c2ecf20Sopenharmony_ci			"Configuration register write error, aborting.\n");
6848c2ecf20Sopenharmony_ci		count = -EIO;
6858c2ecf20Sopenharmony_ci	}
6868c2ecf20Sopenharmony_ciEXIT:
6878c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
6888c2ecf20Sopenharmony_ci	return count;
6898c2ecf20Sopenharmony_ci}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_input, temp, IDX_TEMP1_INPUT);
6928c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_min, temp, IDX_TEMP1_MIN);
6938c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_max, temp, IDX_TEMP1_MAX);
6948c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_crit, temp, IDX_TEMP1_CRIT);
6958c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, temp_alarm, IDX_TEMP1_MIN);
6968c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, temp_alarm, IDX_TEMP1_MAX);
6978c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, temp_alarm, IDX_TEMP1_CRIT);
6988c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_input, temp, IDX_TEMP2_INPUT);
6998c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp2_min, temp, IDX_TEMP2_MIN);
7008c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp2_max, temp, IDX_TEMP2_MAX);
7018c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp2_crit, temp, IDX_TEMP2_CRIT);
7028c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_fault, temp2_fault, 0);
7038c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, temp_alarm, IDX_TEMP2_MIN);
7048c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, temp_alarm, IDX_TEMP2_MAX);
7058c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, temp_alarm, IDX_TEMP2_CRIT);
7068c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan1_input, fan, IDX_FAN1_INPUT);
7078c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan1_min, fan, IDX_FAN1_MIN);
7088c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan1_max, fan, IDX_FAN1_MAX);
7098c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan1_fault, fan1_fault, 0);
7108c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan1_div, fan1_div, 0);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(pwm1, pwm1, 0);
7138c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(pwm1_enable, pwm1_enable, 0);
7148c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(pwm1_auto_point1_pwm, pwm1_auto_point_pwm, 0);
7158c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_pwm, pwm1_auto_point_pwm, 1);
7168c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(pwm1_auto_point3_pwm, pwm1_auto_point_pwm, 2);
7178c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(pwm1_auto_channels_temp, pwm1_auto_channels_temp,
7188c2ecf20Sopenharmony_ci			     0);
7198c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RO(temp1_auto_point1_temp, temp_auto_point_temp,
7208c2ecf20Sopenharmony_ci			       1, 0);
7218c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp1_auto_point2_temp, temp_auto_point_temp,
7228c2ecf20Sopenharmony_ci			       1, 1);
7238c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp1_auto_point3_temp, temp_auto_point_temp,
7248c2ecf20Sopenharmony_ci			       1, 2);
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point1_temp, temp_auto_point_temp,
7278c2ecf20Sopenharmony_ci			       2, 0);
7288c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point2_temp, temp_auto_point_temp,
7298c2ecf20Sopenharmony_ci			       2, 1);
7308c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point3_temp, temp_auto_point_temp,
7318c2ecf20Sopenharmony_ci			       2, 2);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_cistatic struct attribute *amc6821_attrs[] = {
7348c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_input.dev_attr.attr,
7358c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_min.dev_attr.attr,
7368c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_max.dev_attr.attr,
7378c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_crit.dev_attr.attr,
7388c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
7398c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
7408c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
7418c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_input.dev_attr.attr,
7428c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_min.dev_attr.attr,
7438c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_max.dev_attr.attr,
7448c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_crit.dev_attr.attr,
7458c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
7468c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
7478c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
7488c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_fault.dev_attr.attr,
7498c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan1_input.dev_attr.attr,
7508c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan1_min.dev_attr.attr,
7518c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan1_max.dev_attr.attr,
7528c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan1_fault.dev_attr.attr,
7538c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan1_div.dev_attr.attr,
7548c2ecf20Sopenharmony_ci	&sensor_dev_attr_pwm1.dev_attr.attr,
7558c2ecf20Sopenharmony_ci	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
7568c2ecf20Sopenharmony_ci	&sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr,
7578c2ecf20Sopenharmony_ci	&sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
7588c2ecf20Sopenharmony_ci	&sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
7598c2ecf20Sopenharmony_ci	&sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr,
7608c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr,
7618c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_auto_point2_temp.dev_attr.attr,
7628c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_auto_point3_temp.dev_attr.attr,
7638c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_auto_point1_temp.dev_attr.attr,
7648c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_auto_point2_temp.dev_attr.attr,
7658c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_auto_point3_temp.dev_attr.attr,
7668c2ecf20Sopenharmony_ci	NULL
7678c2ecf20Sopenharmony_ci};
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(amc6821);
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci/* Return 0 if detection is successful, -ENODEV otherwise */
7728c2ecf20Sopenharmony_cistatic int amc6821_detect(
7738c2ecf20Sopenharmony_ci		struct i2c_client *client,
7748c2ecf20Sopenharmony_ci		struct i2c_board_info *info)
7758c2ecf20Sopenharmony_ci{
7768c2ecf20Sopenharmony_ci	struct i2c_adapter *adapter = client->adapter;
7778c2ecf20Sopenharmony_ci	int address = client->addr;
7788c2ecf20Sopenharmony_ci	int dev_id, comp_id;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	dev_dbg(&adapter->dev, "amc6821_detect called.\n");
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
7838c2ecf20Sopenharmony_ci		dev_dbg(&adapter->dev,
7848c2ecf20Sopenharmony_ci			"amc6821: I2C bus doesn't support byte mode, "
7858c2ecf20Sopenharmony_ci			"skipping.\n");
7868c2ecf20Sopenharmony_ci		return -ENODEV;
7878c2ecf20Sopenharmony_ci	}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	dev_id = i2c_smbus_read_byte_data(client, AMC6821_REG_DEV_ID);
7908c2ecf20Sopenharmony_ci	comp_id = i2c_smbus_read_byte_data(client, AMC6821_REG_COMP_ID);
7918c2ecf20Sopenharmony_ci	if (dev_id != 0x21 || comp_id != 0x49) {
7928c2ecf20Sopenharmony_ci		dev_dbg(&adapter->dev,
7938c2ecf20Sopenharmony_ci			"amc6821: detection failed at 0x%02x.\n",
7948c2ecf20Sopenharmony_ci			address);
7958c2ecf20Sopenharmony_ci		return -ENODEV;
7968c2ecf20Sopenharmony_ci	}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	/*
7998c2ecf20Sopenharmony_ci	 * Bit 7 of the address register is ignored, so we can check the
8008c2ecf20Sopenharmony_ci	 * ID registers again
8018c2ecf20Sopenharmony_ci	 */
8028c2ecf20Sopenharmony_ci	dev_id = i2c_smbus_read_byte_data(client, 0x80 | AMC6821_REG_DEV_ID);
8038c2ecf20Sopenharmony_ci	comp_id = i2c_smbus_read_byte_data(client, 0x80 | AMC6821_REG_COMP_ID);
8048c2ecf20Sopenharmony_ci	if (dev_id != 0x21 || comp_id != 0x49) {
8058c2ecf20Sopenharmony_ci		dev_dbg(&adapter->dev,
8068c2ecf20Sopenharmony_ci			"amc6821: detection failed at 0x%02x.\n",
8078c2ecf20Sopenharmony_ci			address);
8088c2ecf20Sopenharmony_ci		return -ENODEV;
8098c2ecf20Sopenharmony_ci	}
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	dev_info(&adapter->dev, "amc6821: chip found at 0x%02x.\n", address);
8128c2ecf20Sopenharmony_ci	strlcpy(info->type, "amc6821", I2C_NAME_SIZE);
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	return 0;
8158c2ecf20Sopenharmony_ci}
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_cistatic int amc6821_init_client(struct i2c_client *client)
8188c2ecf20Sopenharmony_ci{
8198c2ecf20Sopenharmony_ci	int config;
8208c2ecf20Sopenharmony_ci	int err = -EIO;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	if (init) {
8238c2ecf20Sopenharmony_ci		config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4);
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci		if (config < 0) {
8268c2ecf20Sopenharmony_ci				dev_err(&client->dev,
8278c2ecf20Sopenharmony_ci			"Error reading configuration register, aborting.\n");
8288c2ecf20Sopenharmony_ci				return err;
8298c2ecf20Sopenharmony_ci		}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci		config |= AMC6821_CONF4_MODE;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci		if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF4,
8348c2ecf20Sopenharmony_ci				config)) {
8358c2ecf20Sopenharmony_ci			dev_err(&client->dev,
8368c2ecf20Sopenharmony_ci			"Configuration register write error, aborting.\n");
8378c2ecf20Sopenharmony_ci			return err;
8388c2ecf20Sopenharmony_ci		}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci		config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF3);
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci		if (config < 0) {
8438c2ecf20Sopenharmony_ci			dev_err(&client->dev,
8448c2ecf20Sopenharmony_ci			"Error reading configuration register, aborting.\n");
8458c2ecf20Sopenharmony_ci			return err;
8468c2ecf20Sopenharmony_ci		}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci		dev_info(&client->dev, "Revision %d\n", config & 0x0f);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci		config &= ~AMC6821_CONF3_THERM_FAN_EN;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci		if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF3,
8538c2ecf20Sopenharmony_ci				config)) {
8548c2ecf20Sopenharmony_ci			dev_err(&client->dev,
8558c2ecf20Sopenharmony_ci			"Configuration register write error, aborting.\n");
8568c2ecf20Sopenharmony_ci			return err;
8578c2ecf20Sopenharmony_ci		}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci		config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF2);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci		if (config < 0) {
8628c2ecf20Sopenharmony_ci			dev_err(&client->dev,
8638c2ecf20Sopenharmony_ci			"Error reading configuration register, aborting.\n");
8648c2ecf20Sopenharmony_ci			return err;
8658c2ecf20Sopenharmony_ci		}
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci		config &= ~AMC6821_CONF2_RTFIE;
8688c2ecf20Sopenharmony_ci		config &= ~AMC6821_CONF2_LTOIE;
8698c2ecf20Sopenharmony_ci		config &= ~AMC6821_CONF2_RTOIE;
8708c2ecf20Sopenharmony_ci		if (i2c_smbus_write_byte_data(client,
8718c2ecf20Sopenharmony_ci				AMC6821_REG_CONF2, config)) {
8728c2ecf20Sopenharmony_ci			dev_err(&client->dev,
8738c2ecf20Sopenharmony_ci			"Configuration register write error, aborting.\n");
8748c2ecf20Sopenharmony_ci			return err;
8758c2ecf20Sopenharmony_ci		}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci		config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci		if (config < 0) {
8808c2ecf20Sopenharmony_ci			dev_err(&client->dev,
8818c2ecf20Sopenharmony_ci			"Error reading configuration register, aborting.\n");
8828c2ecf20Sopenharmony_ci			return err;
8838c2ecf20Sopenharmony_ci		}
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci		config &= ~AMC6821_CONF1_THERMOVIE;
8868c2ecf20Sopenharmony_ci		config &= ~AMC6821_CONF1_FANIE;
8878c2ecf20Sopenharmony_ci		config |= AMC6821_CONF1_START;
8888c2ecf20Sopenharmony_ci		if (pwminv)
8898c2ecf20Sopenharmony_ci			config |= AMC6821_CONF1_PWMINV;
8908c2ecf20Sopenharmony_ci		else
8918c2ecf20Sopenharmony_ci			config &= ~AMC6821_CONF1_PWMINV;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci		if (i2c_smbus_write_byte_data(
8948c2ecf20Sopenharmony_ci				client, AMC6821_REG_CONF1, config)) {
8958c2ecf20Sopenharmony_ci			dev_err(&client->dev,
8968c2ecf20Sopenharmony_ci			"Configuration register write error, aborting.\n");
8978c2ecf20Sopenharmony_ci			return err;
8988c2ecf20Sopenharmony_ci		}
8998c2ecf20Sopenharmony_ci	}
9008c2ecf20Sopenharmony_ci	return 0;
9018c2ecf20Sopenharmony_ci}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_cistatic int amc6821_probe(struct i2c_client *client)
9048c2ecf20Sopenharmony_ci{
9058c2ecf20Sopenharmony_ci	struct device *dev = &client->dev;
9068c2ecf20Sopenharmony_ci	struct amc6821_data *data;
9078c2ecf20Sopenharmony_ci	struct device *hwmon_dev;
9088c2ecf20Sopenharmony_ci	int err;
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	data = devm_kzalloc(dev, sizeof(struct amc6821_data), GFP_KERNEL);
9118c2ecf20Sopenharmony_ci	if (!data)
9128c2ecf20Sopenharmony_ci		return -ENOMEM;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	data->client = client;
9158c2ecf20Sopenharmony_ci	mutex_init(&data->update_lock);
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	/*
9188c2ecf20Sopenharmony_ci	 * Initialize the amc6821 chip
9198c2ecf20Sopenharmony_ci	 */
9208c2ecf20Sopenharmony_ci	err = amc6821_init_client(client);
9218c2ecf20Sopenharmony_ci	if (err)
9228c2ecf20Sopenharmony_ci		return err;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
9258c2ecf20Sopenharmony_ci							   data,
9268c2ecf20Sopenharmony_ci							   amc6821_groups);
9278c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(hwmon_dev);
9288c2ecf20Sopenharmony_ci}
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_cistatic const struct i2c_device_id amc6821_id[] = {
9318c2ecf20Sopenharmony_ci	{ "amc6821", amc6821 },
9328c2ecf20Sopenharmony_ci	{ }
9338c2ecf20Sopenharmony_ci};
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, amc6821_id);
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_cistatic struct i2c_driver amc6821_driver = {
9388c2ecf20Sopenharmony_ci	.class = I2C_CLASS_HWMON,
9398c2ecf20Sopenharmony_ci	.driver = {
9408c2ecf20Sopenharmony_ci		.name	= "amc6821",
9418c2ecf20Sopenharmony_ci	},
9428c2ecf20Sopenharmony_ci	.probe_new = amc6821_probe,
9438c2ecf20Sopenharmony_ci	.id_table = amc6821_id,
9448c2ecf20Sopenharmony_ci	.detect = amc6821_detect,
9458c2ecf20Sopenharmony_ci	.address_list = normal_i2c,
9468c2ecf20Sopenharmony_ci};
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_cimodule_i2c_driver(amc6821_driver);
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
9518c2ecf20Sopenharmony_ciMODULE_AUTHOR("T. Mertelj <tomaz.mertelj@guest.arnes.si>");
9528c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Texas Instruments amc6821 hwmon driver");
953