162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * asc7621.c - Part of lm_sensors, Linux kernel modules for hardware monitoring
462306a36Sopenharmony_ci * Copyright (c) 2007, 2010 George Joseph  <george.joseph@fairview5.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/init.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include <linux/jiffies.h>
1162306a36Sopenharmony_ci#include <linux/i2c.h>
1262306a36Sopenharmony_ci#include <linux/hwmon.h>
1362306a36Sopenharmony_ci#include <linux/hwmon-sysfs.h>
1462306a36Sopenharmony_ci#include <linux/err.h>
1562306a36Sopenharmony_ci#include <linux/mutex.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* Addresses to scan */
1862306a36Sopenharmony_cistatic const unsigned short normal_i2c[] = {
1962306a36Sopenharmony_ci	0x2c, 0x2d, 0x2e, I2C_CLIENT_END
2062306a36Sopenharmony_ci};
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cienum asc7621_type {
2362306a36Sopenharmony_ci	asc7621,
2462306a36Sopenharmony_ci	asc7621a
2562306a36Sopenharmony_ci};
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define INTERVAL_HIGH   (HZ + HZ / 2)
2862306a36Sopenharmony_ci#define INTERVAL_LOW    (1 * 60 * HZ)
2962306a36Sopenharmony_ci#define PRI_NONE        0
3062306a36Sopenharmony_ci#define PRI_LOW         1
3162306a36Sopenharmony_ci#define PRI_HIGH        2
3262306a36Sopenharmony_ci#define FIRST_CHIP      asc7621
3362306a36Sopenharmony_ci#define LAST_CHIP       asc7621a
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct asc7621_chip {
3662306a36Sopenharmony_ci	char *name;
3762306a36Sopenharmony_ci	enum asc7621_type chip_type;
3862306a36Sopenharmony_ci	u8 company_reg;
3962306a36Sopenharmony_ci	u8 company_id;
4062306a36Sopenharmony_ci	u8 verstep_reg;
4162306a36Sopenharmony_ci	u8 verstep_id;
4262306a36Sopenharmony_ci	const unsigned short *addresses;
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic struct asc7621_chip asc7621_chips[] = {
4662306a36Sopenharmony_ci	{
4762306a36Sopenharmony_ci		.name = "asc7621",
4862306a36Sopenharmony_ci		.chip_type = asc7621,
4962306a36Sopenharmony_ci		.company_reg = 0x3e,
5062306a36Sopenharmony_ci		.company_id = 0x61,
5162306a36Sopenharmony_ci		.verstep_reg = 0x3f,
5262306a36Sopenharmony_ci		.verstep_id = 0x6c,
5362306a36Sopenharmony_ci		.addresses = normal_i2c,
5462306a36Sopenharmony_ci	 },
5562306a36Sopenharmony_ci	{
5662306a36Sopenharmony_ci		.name = "asc7621a",
5762306a36Sopenharmony_ci		.chip_type = asc7621a,
5862306a36Sopenharmony_ci		.company_reg = 0x3e,
5962306a36Sopenharmony_ci		.company_id = 0x61,
6062306a36Sopenharmony_ci		.verstep_reg = 0x3f,
6162306a36Sopenharmony_ci		.verstep_id = 0x6d,
6262306a36Sopenharmony_ci		.addresses = normal_i2c,
6362306a36Sopenharmony_ci	 },
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/*
6762306a36Sopenharmony_ci * Defines the highest register to be used, not the count.
6862306a36Sopenharmony_ci * The actual count will probably be smaller because of gaps
6962306a36Sopenharmony_ci * in the implementation (unused register locations).
7062306a36Sopenharmony_ci * This define will safely set the array size of both the parameter
7162306a36Sopenharmony_ci * and data arrays.
7262306a36Sopenharmony_ci * This comes from the data sheet register description table.
7362306a36Sopenharmony_ci */
7462306a36Sopenharmony_ci#define LAST_REGISTER 0xff
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistruct asc7621_data {
7762306a36Sopenharmony_ci	struct i2c_client client;
7862306a36Sopenharmony_ci	struct device *class_dev;
7962306a36Sopenharmony_ci	struct mutex update_lock;
8062306a36Sopenharmony_ci	bool valid;		/* true if following fields are valid */
8162306a36Sopenharmony_ci	unsigned long last_high_reading;	/* In jiffies */
8262306a36Sopenharmony_ci	unsigned long last_low_reading;		/* In jiffies */
8362306a36Sopenharmony_ci	/*
8462306a36Sopenharmony_ci	 * Registers we care about occupy the corresponding index
8562306a36Sopenharmony_ci	 * in the array.  Registers we don't care about are left
8662306a36Sopenharmony_ci	 * at 0.
8762306a36Sopenharmony_ci	 */
8862306a36Sopenharmony_ci	u8 reg[LAST_REGISTER + 1];
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/*
9262306a36Sopenharmony_ci * Macro to get the parent asc7621_param structure
9362306a36Sopenharmony_ci * from a sensor_device_attribute passed into the
9462306a36Sopenharmony_ci * show/store functions.
9562306a36Sopenharmony_ci */
9662306a36Sopenharmony_ci#define to_asc7621_param(_sda) \
9762306a36Sopenharmony_ci	container_of(_sda, struct asc7621_param, sda)
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/*
10062306a36Sopenharmony_ci * Each parameter to be retrieved needs an asc7621_param structure
10162306a36Sopenharmony_ci * allocated.  It contains the sensor_device_attribute structure
10262306a36Sopenharmony_ci * and the control info needed to retrieve the value from the register map.
10362306a36Sopenharmony_ci */
10462306a36Sopenharmony_cistruct asc7621_param {
10562306a36Sopenharmony_ci	struct sensor_device_attribute sda;
10662306a36Sopenharmony_ci	u8 priority;
10762306a36Sopenharmony_ci	u8 msb[3];
10862306a36Sopenharmony_ci	u8 lsb[3];
10962306a36Sopenharmony_ci	u8 mask[3];
11062306a36Sopenharmony_ci	u8 shift[3];
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci/*
11462306a36Sopenharmony_ci * This is the map that ultimately indicates whether we'll be
11562306a36Sopenharmony_ci * retrieving a register value or not, and at what frequency.
11662306a36Sopenharmony_ci */
11762306a36Sopenharmony_cistatic u8 asc7621_register_priorities[255];
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic struct asc7621_data *asc7621_update_device(struct device *dev);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic inline u8 read_byte(struct i2c_client *client, u8 reg)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	int res = i2c_smbus_read_byte_data(client, reg);
12462306a36Sopenharmony_ci	if (res < 0) {
12562306a36Sopenharmony_ci		dev_err(&client->dev,
12662306a36Sopenharmony_ci			"Unable to read from register 0x%02x.\n", reg);
12762306a36Sopenharmony_ci		return 0;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci	return res & 0xff;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic inline int write_byte(struct i2c_client *client, u8 reg, u8 data)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	int res = i2c_smbus_write_byte_data(client, reg, data);
13562306a36Sopenharmony_ci	if (res < 0) {
13662306a36Sopenharmony_ci		dev_err(&client->dev,
13762306a36Sopenharmony_ci			"Unable to write value 0x%02x to register 0x%02x.\n",
13862306a36Sopenharmony_ci			data, reg);
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci	return res;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/*
14462306a36Sopenharmony_ci * Data Handlers
14562306a36Sopenharmony_ci * Each function handles the formatting, storage
14662306a36Sopenharmony_ci * and retrieval of like parameters.
14762306a36Sopenharmony_ci */
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci#define SETUP_SHOW_DATA_PARAM(d, a) \
15062306a36Sopenharmony_ci	struct sensor_device_attribute *sda = to_sensor_dev_attr(a); \
15162306a36Sopenharmony_ci	struct asc7621_data *data = asc7621_update_device(d); \
15262306a36Sopenharmony_ci	struct asc7621_param *param = to_asc7621_param(sda)
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci#define SETUP_STORE_DATA_PARAM(d, a) \
15562306a36Sopenharmony_ci	struct sensor_device_attribute *sda = to_sensor_dev_attr(a); \
15662306a36Sopenharmony_ci	struct i2c_client *client = to_i2c_client(d); \
15762306a36Sopenharmony_ci	struct asc7621_data *data = i2c_get_clientdata(client); \
15862306a36Sopenharmony_ci	struct asc7621_param *param = to_asc7621_param(sda)
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci/*
16162306a36Sopenharmony_ci * u8 is just what it sounds like...an unsigned byte with no
16262306a36Sopenharmony_ci * special formatting.
16362306a36Sopenharmony_ci */
16462306a36Sopenharmony_cistatic ssize_t show_u8(struct device *dev, struct device_attribute *attr,
16562306a36Sopenharmony_ci		       char *buf)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	SETUP_SHOW_DATA_PARAM(dev, attr);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	return sprintf(buf, "%u\n", data->reg[param->msb[0]]);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic ssize_t store_u8(struct device *dev, struct device_attribute *attr,
17362306a36Sopenharmony_ci			const char *buf, size_t count)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	SETUP_STORE_DATA_PARAM(dev, attr);
17662306a36Sopenharmony_ci	long reqval;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (kstrtol(buf, 10, &reqval))
17962306a36Sopenharmony_ci		return -EINVAL;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	reqval = clamp_val(reqval, 0, 255);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
18462306a36Sopenharmony_ci	data->reg[param->msb[0]] = reqval;
18562306a36Sopenharmony_ci	write_byte(client, param->msb[0], reqval);
18662306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
18762306a36Sopenharmony_ci	return count;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/*
19162306a36Sopenharmony_ci * Many of the config values occupy only a few bits of a register.
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_cistatic ssize_t show_bitmask(struct device *dev,
19462306a36Sopenharmony_ci			    struct device_attribute *attr, char *buf)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	SETUP_SHOW_DATA_PARAM(dev, attr);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return sprintf(buf, "%u\n",
19962306a36Sopenharmony_ci		       (data->reg[param->msb[0]] >> param->
20062306a36Sopenharmony_ci			shift[0]) & param->mask[0]);
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic ssize_t store_bitmask(struct device *dev,
20462306a36Sopenharmony_ci			     struct device_attribute *attr,
20562306a36Sopenharmony_ci			     const char *buf, size_t count)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	SETUP_STORE_DATA_PARAM(dev, attr);
20862306a36Sopenharmony_ci	long reqval;
20962306a36Sopenharmony_ci	u8 currval;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (kstrtol(buf, 10, &reqval))
21262306a36Sopenharmony_ci		return -EINVAL;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	reqval = clamp_val(reqval, 0, param->mask[0]);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	reqval = (reqval & param->mask[0]) << param->shift[0];
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
21962306a36Sopenharmony_ci	currval = read_byte(client, param->msb[0]);
22062306a36Sopenharmony_ci	reqval |= (currval & ~(param->mask[0] << param->shift[0]));
22162306a36Sopenharmony_ci	data->reg[param->msb[0]] = reqval;
22262306a36Sopenharmony_ci	write_byte(client, param->msb[0], reqval);
22362306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
22462306a36Sopenharmony_ci	return count;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci/*
22862306a36Sopenharmony_ci * 16 bit fan rpm values
22962306a36Sopenharmony_ci * reported by the device as the number of 11.111us periods (90khz)
23062306a36Sopenharmony_ci * between full fan rotations.  Therefore...
23162306a36Sopenharmony_ci * RPM = (90000 * 60) / register value
23262306a36Sopenharmony_ci */
23362306a36Sopenharmony_cistatic ssize_t show_fan16(struct device *dev,
23462306a36Sopenharmony_ci			  struct device_attribute *attr, char *buf)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	SETUP_SHOW_DATA_PARAM(dev, attr);
23762306a36Sopenharmony_ci	u16 regval;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
24062306a36Sopenharmony_ci	regval = (data->reg[param->msb[0]] << 8) | data->reg[param->lsb[0]];
24162306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	return sprintf(buf, "%u\n",
24462306a36Sopenharmony_ci		       (regval == 0 ? -1 : (regval) ==
24562306a36Sopenharmony_ci			0xffff ? 0 : 5400000 / regval));
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic ssize_t store_fan16(struct device *dev,
24962306a36Sopenharmony_ci			   struct device_attribute *attr, const char *buf,
25062306a36Sopenharmony_ci			   size_t count)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	SETUP_STORE_DATA_PARAM(dev, attr);
25362306a36Sopenharmony_ci	long reqval;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	if (kstrtol(buf, 10, &reqval))
25662306a36Sopenharmony_ci		return -EINVAL;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/*
25962306a36Sopenharmony_ci	 * If a minimum RPM of zero is requested, then we set the register to
26062306a36Sopenharmony_ci	 * 0xffff. This value allows the fan to be stopped completely without
26162306a36Sopenharmony_ci	 * generating an alarm.
26262306a36Sopenharmony_ci	 */
26362306a36Sopenharmony_ci	reqval =
26462306a36Sopenharmony_ci	    (reqval <= 0 ? 0xffff : clamp_val(5400000 / reqval, 0, 0xfffe));
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
26762306a36Sopenharmony_ci	data->reg[param->msb[0]] = (reqval >> 8) & 0xff;
26862306a36Sopenharmony_ci	data->reg[param->lsb[0]] = reqval & 0xff;
26962306a36Sopenharmony_ci	write_byte(client, param->msb[0], data->reg[param->msb[0]]);
27062306a36Sopenharmony_ci	write_byte(client, param->lsb[0], data->reg[param->lsb[0]]);
27162306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	return count;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/*
27762306a36Sopenharmony_ci * Voltages are scaled in the device so that the nominal voltage
27862306a36Sopenharmony_ci * is 3/4ths of the 0-255 range (i.e. 192).
27962306a36Sopenharmony_ci * If all voltages are 'normal' then all voltage registers will
28062306a36Sopenharmony_ci * read 0xC0.
28162306a36Sopenharmony_ci *
28262306a36Sopenharmony_ci * The data sheet provides us with the 3/4 scale value for each voltage
28362306a36Sopenharmony_ci * which is stored in in_scaling.  The sda->index parameter value provides
28462306a36Sopenharmony_ci * the index into in_scaling.
28562306a36Sopenharmony_ci *
28662306a36Sopenharmony_ci * NOTE: The chip expects the first 2 inputs be 2.5 and 2.25 volts
28762306a36Sopenharmony_ci * respectively. That doesn't mean that's what the motherboard provides. :)
28862306a36Sopenharmony_ci */
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic const int asc7621_in_scaling[] = {
29162306a36Sopenharmony_ci	2500, 2250, 3300, 5000, 12000
29262306a36Sopenharmony_ci};
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic ssize_t show_in10(struct device *dev, struct device_attribute *attr,
29562306a36Sopenharmony_ci			 char *buf)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	SETUP_SHOW_DATA_PARAM(dev, attr);
29862306a36Sopenharmony_ci	u16 regval;
29962306a36Sopenharmony_ci	u8 nr = sda->index;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
30262306a36Sopenharmony_ci	regval = (data->reg[param->msb[0]] << 8) | (data->reg[param->lsb[0]]);
30362306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/* The LSB value is a 2-bit scaling of the MSB's LSbit value. */
30662306a36Sopenharmony_ci	regval = (regval >> 6) * asc7621_in_scaling[nr] / (0xc0 << 2);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	return sprintf(buf, "%u\n", regval);
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci/* 8 bit voltage values (the mins and maxs) */
31262306a36Sopenharmony_cistatic ssize_t show_in8(struct device *dev, struct device_attribute *attr,
31362306a36Sopenharmony_ci			char *buf)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	SETUP_SHOW_DATA_PARAM(dev, attr);
31662306a36Sopenharmony_ci	u8 nr = sda->index;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return sprintf(buf, "%u\n",
31962306a36Sopenharmony_ci		       ((data->reg[param->msb[0]] *
32062306a36Sopenharmony_ci			 asc7621_in_scaling[nr]) / 0xc0));
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic ssize_t store_in8(struct device *dev, struct device_attribute *attr,
32462306a36Sopenharmony_ci			 const char *buf, size_t count)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	SETUP_STORE_DATA_PARAM(dev, attr);
32762306a36Sopenharmony_ci	long reqval;
32862306a36Sopenharmony_ci	u8 nr = sda->index;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (kstrtol(buf, 10, &reqval))
33162306a36Sopenharmony_ci		return -EINVAL;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	reqval = clamp_val(reqval, 0, 0xffff);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	reqval = reqval * 0xc0 / asc7621_in_scaling[nr];
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	reqval = clamp_val(reqval, 0, 0xff);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
34062306a36Sopenharmony_ci	data->reg[param->msb[0]] = reqval;
34162306a36Sopenharmony_ci	write_byte(client, param->msb[0], reqval);
34262306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return count;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic ssize_t show_temp8(struct device *dev,
34862306a36Sopenharmony_ci			  struct device_attribute *attr, char *buf)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	SETUP_SHOW_DATA_PARAM(dev, attr);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	return sprintf(buf, "%d\n", ((s8) data->reg[param->msb[0]]) * 1000);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic ssize_t store_temp8(struct device *dev,
35662306a36Sopenharmony_ci			   struct device_attribute *attr, const char *buf,
35762306a36Sopenharmony_ci			   size_t count)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	SETUP_STORE_DATA_PARAM(dev, attr);
36062306a36Sopenharmony_ci	long reqval;
36162306a36Sopenharmony_ci	s8 temp;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (kstrtol(buf, 10, &reqval))
36462306a36Sopenharmony_ci		return -EINVAL;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	reqval = clamp_val(reqval, -127000, 127000);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	temp = reqval / 1000;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
37162306a36Sopenharmony_ci	data->reg[param->msb[0]] = temp;
37262306a36Sopenharmony_ci	write_byte(client, param->msb[0], temp);
37362306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
37462306a36Sopenharmony_ci	return count;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci/*
37862306a36Sopenharmony_ci * Temperatures that occupy 2 bytes always have the whole
37962306a36Sopenharmony_ci * number of degrees in the MSB with some part of the LSB
38062306a36Sopenharmony_ci * indicating fractional degrees.
38162306a36Sopenharmony_ci */
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci/*   mmmmmmmm.llxxxxxx */
38462306a36Sopenharmony_cistatic ssize_t show_temp10(struct device *dev,
38562306a36Sopenharmony_ci			   struct device_attribute *attr, char *buf)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	SETUP_SHOW_DATA_PARAM(dev, attr);
38862306a36Sopenharmony_ci	u8 msb, lsb;
38962306a36Sopenharmony_ci	int temp;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
39262306a36Sopenharmony_ci	msb = data->reg[param->msb[0]];
39362306a36Sopenharmony_ci	lsb = (data->reg[param->lsb[0]] >> 6) & 0x03;
39462306a36Sopenharmony_ci	temp = (((s8) msb) * 1000) + (lsb * 250);
39562306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	return sprintf(buf, "%d\n", temp);
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci/*   mmmmmm.ll */
40162306a36Sopenharmony_cistatic ssize_t show_temp62(struct device *dev,
40262306a36Sopenharmony_ci			   struct device_attribute *attr, char *buf)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	SETUP_SHOW_DATA_PARAM(dev, attr);
40562306a36Sopenharmony_ci	u8 regval = data->reg[param->msb[0]];
40662306a36Sopenharmony_ci	int temp = ((s8) (regval & 0xfc) * 1000) + ((regval & 0x03) * 250);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	return sprintf(buf, "%d\n", temp);
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic ssize_t store_temp62(struct device *dev,
41262306a36Sopenharmony_ci			    struct device_attribute *attr, const char *buf,
41362306a36Sopenharmony_ci			    size_t count)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	SETUP_STORE_DATA_PARAM(dev, attr);
41662306a36Sopenharmony_ci	long reqval, i, f;
41762306a36Sopenharmony_ci	s8 temp;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if (kstrtol(buf, 10, &reqval))
42062306a36Sopenharmony_ci		return -EINVAL;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	reqval = clamp_val(reqval, -32000, 31750);
42362306a36Sopenharmony_ci	i = reqval / 1000;
42462306a36Sopenharmony_ci	f = reqval - (i * 1000);
42562306a36Sopenharmony_ci	temp = i << 2;
42662306a36Sopenharmony_ci	temp |= f / 250;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
42962306a36Sopenharmony_ci	data->reg[param->msb[0]] = temp;
43062306a36Sopenharmony_ci	write_byte(client, param->msb[0], temp);
43162306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
43262306a36Sopenharmony_ci	return count;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci/*
43662306a36Sopenharmony_ci * The aSC7621 doesn't provide an "auto_point2".  Instead, you
43762306a36Sopenharmony_ci * specify the auto_point1 and a range.  To keep with the sysfs
43862306a36Sopenharmony_ci * hwmon specs, we synthesize the auto_point_2 from them.
43962306a36Sopenharmony_ci */
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cistatic const u32 asc7621_range_map[] = {
44262306a36Sopenharmony_ci	2000, 2500, 3330, 4000, 5000, 6670, 8000, 10000,
44362306a36Sopenharmony_ci	13330, 16000, 20000, 26670, 32000, 40000, 53330, 80000,
44462306a36Sopenharmony_ci};
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic ssize_t show_ap2_temp(struct device *dev,
44762306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	SETUP_SHOW_DATA_PARAM(dev, attr);
45062306a36Sopenharmony_ci	long auto_point1;
45162306a36Sopenharmony_ci	u8 regval;
45262306a36Sopenharmony_ci	int temp;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
45562306a36Sopenharmony_ci	auto_point1 = ((s8) data->reg[param->msb[1]]) * 1000;
45662306a36Sopenharmony_ci	regval =
45762306a36Sopenharmony_ci	    ((data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0]);
45862306a36Sopenharmony_ci	temp = auto_point1 + asc7621_range_map[clamp_val(regval, 0, 15)];
45962306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	return sprintf(buf, "%d\n", temp);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic ssize_t store_ap2_temp(struct device *dev,
46662306a36Sopenharmony_ci			      struct device_attribute *attr,
46762306a36Sopenharmony_ci			      const char *buf, size_t count)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	SETUP_STORE_DATA_PARAM(dev, attr);
47062306a36Sopenharmony_ci	long reqval, auto_point1;
47162306a36Sopenharmony_ci	int i;
47262306a36Sopenharmony_ci	u8 currval, newval = 0;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	if (kstrtol(buf, 10, &reqval))
47562306a36Sopenharmony_ci		return -EINVAL;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
47862306a36Sopenharmony_ci	auto_point1 = data->reg[param->msb[1]] * 1000;
47962306a36Sopenharmony_ci	reqval = clamp_val(reqval, auto_point1 + 2000, auto_point1 + 80000);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	for (i = ARRAY_SIZE(asc7621_range_map) - 1; i >= 0; i--) {
48262306a36Sopenharmony_ci		if (reqval >= auto_point1 + asc7621_range_map[i]) {
48362306a36Sopenharmony_ci			newval = i;
48462306a36Sopenharmony_ci			break;
48562306a36Sopenharmony_ci		}
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	newval = (newval & param->mask[0]) << param->shift[0];
48962306a36Sopenharmony_ci	currval = read_byte(client, param->msb[0]);
49062306a36Sopenharmony_ci	newval |= (currval & ~(param->mask[0] << param->shift[0]));
49162306a36Sopenharmony_ci	data->reg[param->msb[0]] = newval;
49262306a36Sopenharmony_ci	write_byte(client, param->msb[0], newval);
49362306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
49462306a36Sopenharmony_ci	return count;
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic ssize_t show_pwm_ac(struct device *dev,
49862306a36Sopenharmony_ci			   struct device_attribute *attr, char *buf)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	SETUP_SHOW_DATA_PARAM(dev, attr);
50162306a36Sopenharmony_ci	u8 config, altbit, regval;
50262306a36Sopenharmony_ci	static const u8 map[] = {
50362306a36Sopenharmony_ci		0x01, 0x02, 0x04, 0x1f, 0x00, 0x06, 0x07, 0x10,
50462306a36Sopenharmony_ci		0x08, 0x0f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f
50562306a36Sopenharmony_ci	};
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
50862306a36Sopenharmony_ci	config = (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
50962306a36Sopenharmony_ci	altbit = (data->reg[param->msb[1]] >> param->shift[1]) & param->mask[1];
51062306a36Sopenharmony_ci	regval = config | (altbit << 3);
51162306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	return sprintf(buf, "%u\n", map[clamp_val(regval, 0, 15)]);
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic ssize_t store_pwm_ac(struct device *dev,
51762306a36Sopenharmony_ci			    struct device_attribute *attr,
51862306a36Sopenharmony_ci			    const char *buf, size_t count)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	SETUP_STORE_DATA_PARAM(dev, attr);
52162306a36Sopenharmony_ci	unsigned long reqval;
52262306a36Sopenharmony_ci	u8 currval, config, altbit, newval;
52362306a36Sopenharmony_ci	static const u16 map[] = {
52462306a36Sopenharmony_ci		0x04, 0x00, 0x01, 0xff, 0x02, 0xff, 0x05, 0x06,
52562306a36Sopenharmony_ci		0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f,
52662306a36Sopenharmony_ci		0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
52762306a36Sopenharmony_ci		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03,
52862306a36Sopenharmony_ci	};
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	if (kstrtoul(buf, 10, &reqval))
53162306a36Sopenharmony_ci		return -EINVAL;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (reqval > 31)
53462306a36Sopenharmony_ci		return -EINVAL;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	reqval = map[reqval];
53762306a36Sopenharmony_ci	if (reqval == 0xff)
53862306a36Sopenharmony_ci		return -EINVAL;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	config = reqval & 0x07;
54162306a36Sopenharmony_ci	altbit = (reqval >> 3) & 0x01;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	config = (config & param->mask[0]) << param->shift[0];
54462306a36Sopenharmony_ci	altbit = (altbit & param->mask[1]) << param->shift[1];
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
54762306a36Sopenharmony_ci	currval = read_byte(client, param->msb[0]);
54862306a36Sopenharmony_ci	newval = config | (currval & ~(param->mask[0] << param->shift[0]));
54962306a36Sopenharmony_ci	newval = altbit | (newval & ~(param->mask[1] << param->shift[1]));
55062306a36Sopenharmony_ci	data->reg[param->msb[0]] = newval;
55162306a36Sopenharmony_ci	write_byte(client, param->msb[0], newval);
55262306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
55362306a36Sopenharmony_ci	return count;
55462306a36Sopenharmony_ci}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_cistatic ssize_t show_pwm_enable(struct device *dev,
55762306a36Sopenharmony_ci			       struct device_attribute *attr, char *buf)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	SETUP_SHOW_DATA_PARAM(dev, attr);
56062306a36Sopenharmony_ci	u8 config, altbit, minoff, val, newval;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
56362306a36Sopenharmony_ci	config = (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
56462306a36Sopenharmony_ci	altbit = (data->reg[param->msb[1]] >> param->shift[1]) & param->mask[1];
56562306a36Sopenharmony_ci	minoff = (data->reg[param->msb[2]] >> param->shift[2]) & param->mask[2];
56662306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	val = config | (altbit << 3);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if (val == 3 || val >= 10)
57162306a36Sopenharmony_ci		newval = 255;
57262306a36Sopenharmony_ci	else if (val == 4)
57362306a36Sopenharmony_ci		newval = 0;
57462306a36Sopenharmony_ci	else if (val == 7)
57562306a36Sopenharmony_ci		newval = 1;
57662306a36Sopenharmony_ci	else if (minoff == 1)
57762306a36Sopenharmony_ci		newval = 2;
57862306a36Sopenharmony_ci	else
57962306a36Sopenharmony_ci		newval = 3;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	return sprintf(buf, "%u\n", newval);
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic ssize_t store_pwm_enable(struct device *dev,
58562306a36Sopenharmony_ci				struct device_attribute *attr,
58662306a36Sopenharmony_ci				const char *buf, size_t count)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	SETUP_STORE_DATA_PARAM(dev, attr);
58962306a36Sopenharmony_ci	long reqval;
59062306a36Sopenharmony_ci	u8 currval, config, altbit, newval, minoff = 255;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	if (kstrtol(buf, 10, &reqval))
59362306a36Sopenharmony_ci		return -EINVAL;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	switch (reqval) {
59662306a36Sopenharmony_ci	case 0:
59762306a36Sopenharmony_ci		newval = 0x04;
59862306a36Sopenharmony_ci		break;
59962306a36Sopenharmony_ci	case 1:
60062306a36Sopenharmony_ci		newval = 0x07;
60162306a36Sopenharmony_ci		break;
60262306a36Sopenharmony_ci	case 2:
60362306a36Sopenharmony_ci		newval = 0x00;
60462306a36Sopenharmony_ci		minoff = 1;
60562306a36Sopenharmony_ci		break;
60662306a36Sopenharmony_ci	case 3:
60762306a36Sopenharmony_ci		newval = 0x00;
60862306a36Sopenharmony_ci		minoff = 0;
60962306a36Sopenharmony_ci		break;
61062306a36Sopenharmony_ci	case 255:
61162306a36Sopenharmony_ci		newval = 0x03;
61262306a36Sopenharmony_ci		break;
61362306a36Sopenharmony_ci	default:
61462306a36Sopenharmony_ci		return -EINVAL;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	config = newval & 0x07;
61862306a36Sopenharmony_ci	altbit = (newval >> 3) & 0x01;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
62162306a36Sopenharmony_ci	config = (config & param->mask[0]) << param->shift[0];
62262306a36Sopenharmony_ci	altbit = (altbit & param->mask[1]) << param->shift[1];
62362306a36Sopenharmony_ci	currval = read_byte(client, param->msb[0]);
62462306a36Sopenharmony_ci	newval = config | (currval & ~(param->mask[0] << param->shift[0]));
62562306a36Sopenharmony_ci	newval = altbit | (newval & ~(param->mask[1] << param->shift[1]));
62662306a36Sopenharmony_ci	data->reg[param->msb[0]] = newval;
62762306a36Sopenharmony_ci	write_byte(client, param->msb[0], newval);
62862306a36Sopenharmony_ci	if (minoff < 255) {
62962306a36Sopenharmony_ci		minoff = (minoff & param->mask[2]) << param->shift[2];
63062306a36Sopenharmony_ci		currval = read_byte(client, param->msb[2]);
63162306a36Sopenharmony_ci		newval =
63262306a36Sopenharmony_ci		    minoff | (currval & ~(param->mask[2] << param->shift[2]));
63362306a36Sopenharmony_ci		data->reg[param->msb[2]] = newval;
63462306a36Sopenharmony_ci		write_byte(client, param->msb[2], newval);
63562306a36Sopenharmony_ci	}
63662306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
63762306a36Sopenharmony_ci	return count;
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic const u32 asc7621_pwm_freq_map[] = {
64162306a36Sopenharmony_ci	10, 15, 23, 30, 38, 47, 62, 94,
64262306a36Sopenharmony_ci	23000, 24000, 25000, 26000, 27000, 28000, 29000, 30000
64362306a36Sopenharmony_ci};
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_cistatic ssize_t show_pwm_freq(struct device *dev,
64662306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	SETUP_SHOW_DATA_PARAM(dev, attr);
64962306a36Sopenharmony_ci	u8 regval =
65062306a36Sopenharmony_ci	    (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	regval = clamp_val(regval, 0, 15);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	return sprintf(buf, "%u\n", asc7621_pwm_freq_map[regval]);
65562306a36Sopenharmony_ci}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_cistatic ssize_t store_pwm_freq(struct device *dev,
65862306a36Sopenharmony_ci			      struct device_attribute *attr,
65962306a36Sopenharmony_ci			      const char *buf, size_t count)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	SETUP_STORE_DATA_PARAM(dev, attr);
66262306a36Sopenharmony_ci	unsigned long reqval;
66362306a36Sopenharmony_ci	u8 currval, newval = 255;
66462306a36Sopenharmony_ci	int i;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	if (kstrtoul(buf, 10, &reqval))
66762306a36Sopenharmony_ci		return -EINVAL;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(asc7621_pwm_freq_map); i++) {
67062306a36Sopenharmony_ci		if (reqval == asc7621_pwm_freq_map[i]) {
67162306a36Sopenharmony_ci			newval = i;
67262306a36Sopenharmony_ci			break;
67362306a36Sopenharmony_ci		}
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci	if (newval == 255)
67662306a36Sopenharmony_ci		return -EINVAL;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	newval = (newval & param->mask[0]) << param->shift[0];
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
68162306a36Sopenharmony_ci	currval = read_byte(client, param->msb[0]);
68262306a36Sopenharmony_ci	newval |= (currval & ~(param->mask[0] << param->shift[0]));
68362306a36Sopenharmony_ci	data->reg[param->msb[0]] = newval;
68462306a36Sopenharmony_ci	write_byte(client, param->msb[0], newval);
68562306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
68662306a36Sopenharmony_ci	return count;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_cistatic const u32 asc7621_pwm_auto_spinup_map[] =  {
69062306a36Sopenharmony_ci	0, 100, 250, 400, 700, 1000, 2000, 4000
69162306a36Sopenharmony_ci};
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic ssize_t show_pwm_ast(struct device *dev,
69462306a36Sopenharmony_ci			    struct device_attribute *attr, char *buf)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci	SETUP_SHOW_DATA_PARAM(dev, attr);
69762306a36Sopenharmony_ci	u8 regval =
69862306a36Sopenharmony_ci	    (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	regval = clamp_val(regval, 0, 7);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	return sprintf(buf, "%u\n", asc7621_pwm_auto_spinup_map[regval]);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic ssize_t store_pwm_ast(struct device *dev,
70762306a36Sopenharmony_ci			     struct device_attribute *attr,
70862306a36Sopenharmony_ci			     const char *buf, size_t count)
70962306a36Sopenharmony_ci{
71062306a36Sopenharmony_ci	SETUP_STORE_DATA_PARAM(dev, attr);
71162306a36Sopenharmony_ci	long reqval;
71262306a36Sopenharmony_ci	u8 currval, newval = 255;
71362306a36Sopenharmony_ci	u32 i;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	if (kstrtol(buf, 10, &reqval))
71662306a36Sopenharmony_ci		return -EINVAL;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(asc7621_pwm_auto_spinup_map); i++) {
71962306a36Sopenharmony_ci		if (reqval == asc7621_pwm_auto_spinup_map[i]) {
72062306a36Sopenharmony_ci			newval = i;
72162306a36Sopenharmony_ci			break;
72262306a36Sopenharmony_ci		}
72362306a36Sopenharmony_ci	}
72462306a36Sopenharmony_ci	if (newval == 255)
72562306a36Sopenharmony_ci		return -EINVAL;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	newval = (newval & param->mask[0]) << param->shift[0];
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
73062306a36Sopenharmony_ci	currval = read_byte(client, param->msb[0]);
73162306a36Sopenharmony_ci	newval |= (currval & ~(param->mask[0] << param->shift[0]));
73262306a36Sopenharmony_ci	data->reg[param->msb[0]] = newval;
73362306a36Sopenharmony_ci	write_byte(client, param->msb[0], newval);
73462306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
73562306a36Sopenharmony_ci	return count;
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic const u32 asc7621_temp_smoothing_time_map[] = {
73962306a36Sopenharmony_ci	35000, 17600, 11800, 7000, 4400, 3000, 1600, 800
74062306a36Sopenharmony_ci};
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_cistatic ssize_t show_temp_st(struct device *dev,
74362306a36Sopenharmony_ci			    struct device_attribute *attr, char *buf)
74462306a36Sopenharmony_ci{
74562306a36Sopenharmony_ci	SETUP_SHOW_DATA_PARAM(dev, attr);
74662306a36Sopenharmony_ci	u8 regval =
74762306a36Sopenharmony_ci	    (data->reg[param->msb[0]] >> param->shift[0]) & param->mask[0];
74862306a36Sopenharmony_ci	regval = clamp_val(regval, 0, 7);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	return sprintf(buf, "%u\n", asc7621_temp_smoothing_time_map[regval]);
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_cistatic ssize_t store_temp_st(struct device *dev,
75462306a36Sopenharmony_ci			     struct device_attribute *attr,
75562306a36Sopenharmony_ci			     const char *buf, size_t count)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	SETUP_STORE_DATA_PARAM(dev, attr);
75862306a36Sopenharmony_ci	long reqval;
75962306a36Sopenharmony_ci	u8 currval, newval = 255;
76062306a36Sopenharmony_ci	u32 i;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	if (kstrtol(buf, 10, &reqval))
76362306a36Sopenharmony_ci		return -EINVAL;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(asc7621_temp_smoothing_time_map); i++) {
76662306a36Sopenharmony_ci		if (reqval == asc7621_temp_smoothing_time_map[i]) {
76762306a36Sopenharmony_ci			newval = i;
76862306a36Sopenharmony_ci			break;
76962306a36Sopenharmony_ci		}
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	if (newval == 255)
77362306a36Sopenharmony_ci		return -EINVAL;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	newval = (newval & param->mask[0]) << param->shift[0];
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
77862306a36Sopenharmony_ci	currval = read_byte(client, param->msb[0]);
77962306a36Sopenharmony_ci	newval |= (currval & ~(param->mask[0] << param->shift[0]));
78062306a36Sopenharmony_ci	data->reg[param->msb[0]] = newval;
78162306a36Sopenharmony_ci	write_byte(client, param->msb[0], newval);
78262306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
78362306a36Sopenharmony_ci	return count;
78462306a36Sopenharmony_ci}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci/*
78762306a36Sopenharmony_ci * End of data handlers
78862306a36Sopenharmony_ci *
78962306a36Sopenharmony_ci * These defines do nothing more than make the table easier
79062306a36Sopenharmony_ci * to read when wrapped at column 80.
79162306a36Sopenharmony_ci */
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci/*
79462306a36Sopenharmony_ci * Creates a variable length array inititalizer.
79562306a36Sopenharmony_ci * VAA(1,3,5,7) would produce {1,3,5,7}
79662306a36Sopenharmony_ci */
79762306a36Sopenharmony_ci#define VAA(args...) {args}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci#define PREAD(name, n, pri, rm, rl, m, s, r) \
80062306a36Sopenharmony_ci	{.sda = SENSOR_ATTR(name, S_IRUGO, show_##r, NULL, n), \
80162306a36Sopenharmony_ci	  .priority = pri, .msb[0] = rm, .lsb[0] = rl, .mask[0] = m, \
80262306a36Sopenharmony_ci	  .shift[0] = s,}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci#define PWRITE(name, n, pri, rm, rl, m, s, r) \
80562306a36Sopenharmony_ci	{.sda = SENSOR_ATTR(name, S_IRUGO | S_IWUSR, show_##r, store_##r, n), \
80662306a36Sopenharmony_ci	  .priority = pri, .msb[0] = rm, .lsb[0] = rl, .mask[0] = m, \
80762306a36Sopenharmony_ci	  .shift[0] = s,}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci/*
81062306a36Sopenharmony_ci * PWRITEM assumes that the initializers for the .msb, .lsb, .mask and .shift
81162306a36Sopenharmony_ci * were created using the VAA macro.
81262306a36Sopenharmony_ci */
81362306a36Sopenharmony_ci#define PWRITEM(name, n, pri, rm, rl, m, s, r) \
81462306a36Sopenharmony_ci	{.sda = SENSOR_ATTR(name, S_IRUGO | S_IWUSR, show_##r, store_##r, n), \
81562306a36Sopenharmony_ci	  .priority = pri, .msb = rm, .lsb = rl, .mask = m, .shift = s,}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_cistatic struct asc7621_param asc7621_params[] = {
81862306a36Sopenharmony_ci	PREAD(in0_input, 0, PRI_HIGH, 0x20, 0x13, 0, 0, in10),
81962306a36Sopenharmony_ci	PREAD(in1_input, 1, PRI_HIGH, 0x21, 0x18, 0, 0, in10),
82062306a36Sopenharmony_ci	PREAD(in2_input, 2, PRI_HIGH, 0x22, 0x11, 0, 0, in10),
82162306a36Sopenharmony_ci	PREAD(in3_input, 3, PRI_HIGH, 0x23, 0x12, 0, 0, in10),
82262306a36Sopenharmony_ci	PREAD(in4_input, 4, PRI_HIGH, 0x24, 0x14, 0, 0, in10),
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	PWRITE(in0_min, 0, PRI_LOW, 0x44, 0, 0, 0, in8),
82562306a36Sopenharmony_ci	PWRITE(in1_min, 1, PRI_LOW, 0x46, 0, 0, 0, in8),
82662306a36Sopenharmony_ci	PWRITE(in2_min, 2, PRI_LOW, 0x48, 0, 0, 0, in8),
82762306a36Sopenharmony_ci	PWRITE(in3_min, 3, PRI_LOW, 0x4a, 0, 0, 0, in8),
82862306a36Sopenharmony_ci	PWRITE(in4_min, 4, PRI_LOW, 0x4c, 0, 0, 0, in8),
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	PWRITE(in0_max, 0, PRI_LOW, 0x45, 0, 0, 0, in8),
83162306a36Sopenharmony_ci	PWRITE(in1_max, 1, PRI_LOW, 0x47, 0, 0, 0, in8),
83262306a36Sopenharmony_ci	PWRITE(in2_max, 2, PRI_LOW, 0x49, 0, 0, 0, in8),
83362306a36Sopenharmony_ci	PWRITE(in3_max, 3, PRI_LOW, 0x4b, 0, 0, 0, in8),
83462306a36Sopenharmony_ci	PWRITE(in4_max, 4, PRI_LOW, 0x4d, 0, 0, 0, in8),
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	PREAD(in0_alarm, 0, PRI_HIGH, 0x41, 0, 0x01, 0, bitmask),
83762306a36Sopenharmony_ci	PREAD(in1_alarm, 1, PRI_HIGH, 0x41, 0, 0x01, 1, bitmask),
83862306a36Sopenharmony_ci	PREAD(in2_alarm, 2, PRI_HIGH, 0x41, 0, 0x01, 2, bitmask),
83962306a36Sopenharmony_ci	PREAD(in3_alarm, 3, PRI_HIGH, 0x41, 0, 0x01, 3, bitmask),
84062306a36Sopenharmony_ci	PREAD(in4_alarm, 4, PRI_HIGH, 0x42, 0, 0x01, 0, bitmask),
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	PREAD(fan1_input, 0, PRI_HIGH, 0x29, 0x28, 0, 0, fan16),
84362306a36Sopenharmony_ci	PREAD(fan2_input, 1, PRI_HIGH, 0x2b, 0x2a, 0, 0, fan16),
84462306a36Sopenharmony_ci	PREAD(fan3_input, 2, PRI_HIGH, 0x2d, 0x2c, 0, 0, fan16),
84562306a36Sopenharmony_ci	PREAD(fan4_input, 3, PRI_HIGH, 0x2f, 0x2e, 0, 0, fan16),
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	PWRITE(fan1_min, 0, PRI_LOW, 0x55, 0x54, 0, 0, fan16),
84862306a36Sopenharmony_ci	PWRITE(fan2_min, 1, PRI_LOW, 0x57, 0x56, 0, 0, fan16),
84962306a36Sopenharmony_ci	PWRITE(fan3_min, 2, PRI_LOW, 0x59, 0x58, 0, 0, fan16),
85062306a36Sopenharmony_ci	PWRITE(fan4_min, 3, PRI_LOW, 0x5b, 0x5a, 0, 0, fan16),
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	PREAD(fan1_alarm, 0, PRI_HIGH, 0x42, 0, 0x01, 2, bitmask),
85362306a36Sopenharmony_ci	PREAD(fan2_alarm, 1, PRI_HIGH, 0x42, 0, 0x01, 3, bitmask),
85462306a36Sopenharmony_ci	PREAD(fan3_alarm, 2, PRI_HIGH, 0x42, 0, 0x01, 4, bitmask),
85562306a36Sopenharmony_ci	PREAD(fan4_alarm, 3, PRI_HIGH, 0x42, 0, 0x01, 5, bitmask),
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	PREAD(temp1_input, 0, PRI_HIGH, 0x25, 0x10, 0, 0, temp10),
85862306a36Sopenharmony_ci	PREAD(temp2_input, 1, PRI_HIGH, 0x26, 0x15, 0, 0, temp10),
85962306a36Sopenharmony_ci	PREAD(temp3_input, 2, PRI_HIGH, 0x27, 0x16, 0, 0, temp10),
86062306a36Sopenharmony_ci	PREAD(temp4_input, 3, PRI_HIGH, 0x33, 0x17, 0, 0, temp10),
86162306a36Sopenharmony_ci	PREAD(temp5_input, 4, PRI_HIGH, 0xf7, 0xf6, 0, 0, temp10),
86262306a36Sopenharmony_ci	PREAD(temp6_input, 5, PRI_HIGH, 0xf9, 0xf8, 0, 0, temp10),
86362306a36Sopenharmony_ci	PREAD(temp7_input, 6, PRI_HIGH, 0xfb, 0xfa, 0, 0, temp10),
86462306a36Sopenharmony_ci	PREAD(temp8_input, 7, PRI_HIGH, 0xfd, 0xfc, 0, 0, temp10),
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	PWRITE(temp1_min, 0, PRI_LOW, 0x4e, 0, 0, 0, temp8),
86762306a36Sopenharmony_ci	PWRITE(temp2_min, 1, PRI_LOW, 0x50, 0, 0, 0, temp8),
86862306a36Sopenharmony_ci	PWRITE(temp3_min, 2, PRI_LOW, 0x52, 0, 0, 0, temp8),
86962306a36Sopenharmony_ci	PWRITE(temp4_min, 3, PRI_LOW, 0x34, 0, 0, 0, temp8),
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	PWRITE(temp1_max, 0, PRI_LOW, 0x4f, 0, 0, 0, temp8),
87262306a36Sopenharmony_ci	PWRITE(temp2_max, 1, PRI_LOW, 0x51, 0, 0, 0, temp8),
87362306a36Sopenharmony_ci	PWRITE(temp3_max, 2, PRI_LOW, 0x53, 0, 0, 0, temp8),
87462306a36Sopenharmony_ci	PWRITE(temp4_max, 3, PRI_LOW, 0x35, 0, 0, 0, temp8),
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	PREAD(temp1_alarm, 0, PRI_HIGH, 0x41, 0, 0x01, 4, bitmask),
87762306a36Sopenharmony_ci	PREAD(temp2_alarm, 1, PRI_HIGH, 0x41, 0, 0x01, 5, bitmask),
87862306a36Sopenharmony_ci	PREAD(temp3_alarm, 2, PRI_HIGH, 0x41, 0, 0x01, 6, bitmask),
87962306a36Sopenharmony_ci	PREAD(temp4_alarm, 3, PRI_HIGH, 0x43, 0, 0x01, 0, bitmask),
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	PWRITE(temp1_source, 0, PRI_LOW, 0x02, 0, 0x07, 4, bitmask),
88262306a36Sopenharmony_ci	PWRITE(temp2_source, 1, PRI_LOW, 0x02, 0, 0x07, 0, bitmask),
88362306a36Sopenharmony_ci	PWRITE(temp3_source, 2, PRI_LOW, 0x03, 0, 0x07, 4, bitmask),
88462306a36Sopenharmony_ci	PWRITE(temp4_source, 3, PRI_LOW, 0x03, 0, 0x07, 0, bitmask),
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	PWRITE(temp1_smoothing_enable, 0, PRI_LOW, 0x62, 0, 0x01, 3, bitmask),
88762306a36Sopenharmony_ci	PWRITE(temp2_smoothing_enable, 1, PRI_LOW, 0x63, 0, 0x01, 7, bitmask),
88862306a36Sopenharmony_ci	PWRITE(temp3_smoothing_enable, 2, PRI_LOW, 0x63, 0, 0x01, 3, bitmask),
88962306a36Sopenharmony_ci	PWRITE(temp4_smoothing_enable, 3, PRI_LOW, 0x3c, 0, 0x01, 3, bitmask),
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	PWRITE(temp1_smoothing_time, 0, PRI_LOW, 0x62, 0, 0x07, 0, temp_st),
89262306a36Sopenharmony_ci	PWRITE(temp2_smoothing_time, 1, PRI_LOW, 0x63, 0, 0x07, 4, temp_st),
89362306a36Sopenharmony_ci	PWRITE(temp3_smoothing_time, 2, PRI_LOW, 0x63, 0, 0x07, 0, temp_st),
89462306a36Sopenharmony_ci	PWRITE(temp4_smoothing_time, 3, PRI_LOW, 0x3c, 0, 0x07, 0, temp_st),
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	PWRITE(temp1_auto_point1_temp_hyst, 0, PRI_LOW, 0x6d, 0, 0x0f, 4,
89762306a36Sopenharmony_ci	       bitmask),
89862306a36Sopenharmony_ci	PWRITE(temp2_auto_point1_temp_hyst, 1, PRI_LOW, 0x6d, 0, 0x0f, 0,
89962306a36Sopenharmony_ci	       bitmask),
90062306a36Sopenharmony_ci	PWRITE(temp3_auto_point1_temp_hyst, 2, PRI_LOW, 0x6e, 0, 0x0f, 4,
90162306a36Sopenharmony_ci	       bitmask),
90262306a36Sopenharmony_ci	PWRITE(temp4_auto_point1_temp_hyst, 3, PRI_LOW, 0x6e, 0, 0x0f, 0,
90362306a36Sopenharmony_ci	       bitmask),
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	PREAD(temp1_auto_point2_temp_hyst, 0, PRI_LOW, 0x6d, 0, 0x0f, 4,
90662306a36Sopenharmony_ci	      bitmask),
90762306a36Sopenharmony_ci	PREAD(temp2_auto_point2_temp_hyst, 1, PRI_LOW, 0x6d, 0, 0x0f, 0,
90862306a36Sopenharmony_ci	      bitmask),
90962306a36Sopenharmony_ci	PREAD(temp3_auto_point2_temp_hyst, 2, PRI_LOW, 0x6e, 0, 0x0f, 4,
91062306a36Sopenharmony_ci	      bitmask),
91162306a36Sopenharmony_ci	PREAD(temp4_auto_point2_temp_hyst, 3, PRI_LOW, 0x6e, 0, 0x0f, 0,
91262306a36Sopenharmony_ci	      bitmask),
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	PWRITE(temp1_auto_point1_temp, 0, PRI_LOW, 0x67, 0, 0, 0, temp8),
91562306a36Sopenharmony_ci	PWRITE(temp2_auto_point1_temp, 1, PRI_LOW, 0x68, 0, 0, 0, temp8),
91662306a36Sopenharmony_ci	PWRITE(temp3_auto_point1_temp, 2, PRI_LOW, 0x69, 0, 0, 0, temp8),
91762306a36Sopenharmony_ci	PWRITE(temp4_auto_point1_temp, 3, PRI_LOW, 0x3b, 0, 0, 0, temp8),
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	PWRITEM(temp1_auto_point2_temp, 0, PRI_LOW, VAA(0x5f, 0x67), VAA(0),
92062306a36Sopenharmony_ci		VAA(0x0f), VAA(4), ap2_temp),
92162306a36Sopenharmony_ci	PWRITEM(temp2_auto_point2_temp, 1, PRI_LOW, VAA(0x60, 0x68), VAA(0),
92262306a36Sopenharmony_ci		VAA(0x0f), VAA(4), ap2_temp),
92362306a36Sopenharmony_ci	PWRITEM(temp3_auto_point2_temp, 2, PRI_LOW, VAA(0x61, 0x69), VAA(0),
92462306a36Sopenharmony_ci		VAA(0x0f), VAA(4), ap2_temp),
92562306a36Sopenharmony_ci	PWRITEM(temp4_auto_point2_temp, 3, PRI_LOW, VAA(0x3c, 0x3b), VAA(0),
92662306a36Sopenharmony_ci		VAA(0x0f), VAA(4), ap2_temp),
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	PWRITE(temp1_crit, 0, PRI_LOW, 0x6a, 0, 0, 0, temp8),
92962306a36Sopenharmony_ci	PWRITE(temp2_crit, 1, PRI_LOW, 0x6b, 0, 0, 0, temp8),
93062306a36Sopenharmony_ci	PWRITE(temp3_crit, 2, PRI_LOW, 0x6c, 0, 0, 0, temp8),
93162306a36Sopenharmony_ci	PWRITE(temp4_crit, 3, PRI_LOW, 0x3d, 0, 0, 0, temp8),
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	PWRITE(temp5_enable, 4, PRI_LOW, 0x0e, 0, 0x01, 0, bitmask),
93462306a36Sopenharmony_ci	PWRITE(temp6_enable, 5, PRI_LOW, 0x0e, 0, 0x01, 1, bitmask),
93562306a36Sopenharmony_ci	PWRITE(temp7_enable, 6, PRI_LOW, 0x0e, 0, 0x01, 2, bitmask),
93662306a36Sopenharmony_ci	PWRITE(temp8_enable, 7, PRI_LOW, 0x0e, 0, 0x01, 3, bitmask),
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	PWRITE(remote1_offset, 0, PRI_LOW, 0x1c, 0, 0, 0, temp62),
93962306a36Sopenharmony_ci	PWRITE(remote2_offset, 1, PRI_LOW, 0x1d, 0, 0, 0, temp62),
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	PWRITE(pwm1, 0, PRI_HIGH, 0x30, 0, 0, 0, u8),
94262306a36Sopenharmony_ci	PWRITE(pwm2, 1, PRI_HIGH, 0x31, 0, 0, 0, u8),
94362306a36Sopenharmony_ci	PWRITE(pwm3, 2, PRI_HIGH, 0x32, 0, 0, 0, u8),
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	PWRITE(pwm1_invert, 0, PRI_LOW, 0x5c, 0, 0x01, 4, bitmask),
94662306a36Sopenharmony_ci	PWRITE(pwm2_invert, 1, PRI_LOW, 0x5d, 0, 0x01, 4, bitmask),
94762306a36Sopenharmony_ci	PWRITE(pwm3_invert, 2, PRI_LOW, 0x5e, 0, 0x01, 4, bitmask),
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	PWRITEM(pwm1_enable, 0, PRI_LOW, VAA(0x5c, 0x5c, 0x62), VAA(0, 0, 0),
95062306a36Sopenharmony_ci		VAA(0x07, 0x01, 0x01), VAA(5, 3, 5), pwm_enable),
95162306a36Sopenharmony_ci	PWRITEM(pwm2_enable, 1, PRI_LOW, VAA(0x5d, 0x5d, 0x62), VAA(0, 0, 0),
95262306a36Sopenharmony_ci		VAA(0x07, 0x01, 0x01), VAA(5, 3, 6), pwm_enable),
95362306a36Sopenharmony_ci	PWRITEM(pwm3_enable, 2, PRI_LOW, VAA(0x5e, 0x5e, 0x62), VAA(0, 0, 0),
95462306a36Sopenharmony_ci		VAA(0x07, 0x01, 0x01), VAA(5, 3, 7), pwm_enable),
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	PWRITEM(pwm1_auto_channels, 0, PRI_LOW, VAA(0x5c, 0x5c), VAA(0, 0),
95762306a36Sopenharmony_ci		VAA(0x07, 0x01), VAA(5, 3), pwm_ac),
95862306a36Sopenharmony_ci	PWRITEM(pwm2_auto_channels, 1, PRI_LOW, VAA(0x5d, 0x5d), VAA(0, 0),
95962306a36Sopenharmony_ci		VAA(0x07, 0x01), VAA(5, 3), pwm_ac),
96062306a36Sopenharmony_ci	PWRITEM(pwm3_auto_channels, 2, PRI_LOW, VAA(0x5e, 0x5e), VAA(0, 0),
96162306a36Sopenharmony_ci		VAA(0x07, 0x01), VAA(5, 3), pwm_ac),
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	PWRITE(pwm1_auto_point1_pwm, 0, PRI_LOW, 0x64, 0, 0, 0, u8),
96462306a36Sopenharmony_ci	PWRITE(pwm2_auto_point1_pwm, 1, PRI_LOW, 0x65, 0, 0, 0, u8),
96562306a36Sopenharmony_ci	PWRITE(pwm3_auto_point1_pwm, 2, PRI_LOW, 0x66, 0, 0, 0, u8),
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	PWRITE(pwm1_auto_point2_pwm, 0, PRI_LOW, 0x38, 0, 0, 0, u8),
96862306a36Sopenharmony_ci	PWRITE(pwm2_auto_point2_pwm, 1, PRI_LOW, 0x39, 0, 0, 0, u8),
96962306a36Sopenharmony_ci	PWRITE(pwm3_auto_point2_pwm, 2, PRI_LOW, 0x3a, 0, 0, 0, u8),
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	PWRITE(pwm1_freq, 0, PRI_LOW, 0x5f, 0, 0x0f, 0, pwm_freq),
97262306a36Sopenharmony_ci	PWRITE(pwm2_freq, 1, PRI_LOW, 0x60, 0, 0x0f, 0, pwm_freq),
97362306a36Sopenharmony_ci	PWRITE(pwm3_freq, 2, PRI_LOW, 0x61, 0, 0x0f, 0, pwm_freq),
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	PREAD(pwm1_auto_zone_assigned, 0, PRI_LOW, 0, 0, 0x03, 2, bitmask),
97662306a36Sopenharmony_ci	PREAD(pwm2_auto_zone_assigned, 1, PRI_LOW, 0, 0, 0x03, 4, bitmask),
97762306a36Sopenharmony_ci	PREAD(pwm3_auto_zone_assigned, 2, PRI_LOW, 0, 0, 0x03, 6, bitmask),
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	PWRITE(pwm1_auto_spinup_time, 0, PRI_LOW, 0x5c, 0, 0x07, 0, pwm_ast),
98062306a36Sopenharmony_ci	PWRITE(pwm2_auto_spinup_time, 1, PRI_LOW, 0x5d, 0, 0x07, 0, pwm_ast),
98162306a36Sopenharmony_ci	PWRITE(pwm3_auto_spinup_time, 2, PRI_LOW, 0x5e, 0, 0x07, 0, pwm_ast),
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	PWRITE(peci_enable, 0, PRI_LOW, 0x40, 0, 0x01, 4, bitmask),
98462306a36Sopenharmony_ci	PWRITE(peci_avg, 0, PRI_LOW, 0x36, 0, 0x07, 0, bitmask),
98562306a36Sopenharmony_ci	PWRITE(peci_domain, 0, PRI_LOW, 0x36, 0, 0x01, 3, bitmask),
98662306a36Sopenharmony_ci	PWRITE(peci_legacy, 0, PRI_LOW, 0x36, 0, 0x01, 4, bitmask),
98762306a36Sopenharmony_ci	PWRITE(peci_diode, 0, PRI_LOW, 0x0e, 0, 0x07, 4, bitmask),
98862306a36Sopenharmony_ci	PWRITE(peci_4domain, 0, PRI_LOW, 0x0e, 0, 0x01, 4, bitmask),
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci};
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_cistatic struct asc7621_data *asc7621_update_device(struct device *dev)
99362306a36Sopenharmony_ci{
99462306a36Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
99562306a36Sopenharmony_ci	struct asc7621_data *data = i2c_get_clientdata(client);
99662306a36Sopenharmony_ci	int i;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci/*
99962306a36Sopenharmony_ci * The asc7621 chips guarantee consistent reads of multi-byte values
100062306a36Sopenharmony_ci * regardless of the order of the reads.  No special logic is needed
100162306a36Sopenharmony_ci * so we can just read the registers in whatever  order they appear
100262306a36Sopenharmony_ci * in the asc7621_params array.
100362306a36Sopenharmony_ci */
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	/* Read all the high priority registers */
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	if (!data->valid ||
101062306a36Sopenharmony_ci	    time_after(jiffies, data->last_high_reading + INTERVAL_HIGH)) {
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(asc7621_register_priorities); i++) {
101362306a36Sopenharmony_ci			if (asc7621_register_priorities[i] == PRI_HIGH) {
101462306a36Sopenharmony_ci				data->reg[i] =
101562306a36Sopenharmony_ci				    i2c_smbus_read_byte_data(client, i) & 0xff;
101662306a36Sopenharmony_ci			}
101762306a36Sopenharmony_ci		}
101862306a36Sopenharmony_ci		data->last_high_reading = jiffies;
101962306a36Sopenharmony_ci	}			/* last_reading */
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	/* Read all the low priority registers. */
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	if (!data->valid ||
102462306a36Sopenharmony_ci	    time_after(jiffies, data->last_low_reading + INTERVAL_LOW)) {
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) {
102762306a36Sopenharmony_ci			if (asc7621_register_priorities[i] == PRI_LOW) {
102862306a36Sopenharmony_ci				data->reg[i] =
102962306a36Sopenharmony_ci				    i2c_smbus_read_byte_data(client, i) & 0xff;
103062306a36Sopenharmony_ci			}
103162306a36Sopenharmony_ci		}
103262306a36Sopenharmony_ci		data->last_low_reading = jiffies;
103362306a36Sopenharmony_ci	}			/* last_reading */
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	data->valid = true;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	return data;
104062306a36Sopenharmony_ci}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci/*
104362306a36Sopenharmony_ci * Standard detection and initialization below
104462306a36Sopenharmony_ci *
104562306a36Sopenharmony_ci * Helper function that checks if an address is valid
104662306a36Sopenharmony_ci * for a particular chip.
104762306a36Sopenharmony_ci */
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_cistatic inline int valid_address_for_chip(int chip_type, int address)
105062306a36Sopenharmony_ci{
105162306a36Sopenharmony_ci	int i;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	for (i = 0; asc7621_chips[chip_type].addresses[i] != I2C_CLIENT_END;
105462306a36Sopenharmony_ci	     i++) {
105562306a36Sopenharmony_ci		if (asc7621_chips[chip_type].addresses[i] == address)
105662306a36Sopenharmony_ci			return 1;
105762306a36Sopenharmony_ci	}
105862306a36Sopenharmony_ci	return 0;
105962306a36Sopenharmony_ci}
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_cistatic void asc7621_init_client(struct i2c_client *client)
106262306a36Sopenharmony_ci{
106362306a36Sopenharmony_ci	int value;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	/* Warn if part was not "READY" */
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	value = read_byte(client, 0x40);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	if (value & 0x02) {
107062306a36Sopenharmony_ci		dev_err(&client->dev,
107162306a36Sopenharmony_ci			"Client (%d,0x%02x) config is locked.\n",
107262306a36Sopenharmony_ci			i2c_adapter_id(client->adapter), client->addr);
107362306a36Sopenharmony_ci	}
107462306a36Sopenharmony_ci	if (!(value & 0x04)) {
107562306a36Sopenharmony_ci		dev_err(&client->dev, "Client (%d,0x%02x) is not ready.\n",
107662306a36Sopenharmony_ci			i2c_adapter_id(client->adapter), client->addr);
107762306a36Sopenharmony_ci	}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci/*
108062306a36Sopenharmony_ci * Start monitoring
108162306a36Sopenharmony_ci *
108262306a36Sopenharmony_ci * Try to clear LOCK, Set START, save everything else
108362306a36Sopenharmony_ci */
108462306a36Sopenharmony_ci	value = (value & ~0x02) | 0x01;
108562306a36Sopenharmony_ci	write_byte(client, 0x40, value & 0xff);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_cistatic int
109062306a36Sopenharmony_ciasc7621_probe(struct i2c_client *client)
109162306a36Sopenharmony_ci{
109262306a36Sopenharmony_ci	struct asc7621_data *data;
109362306a36Sopenharmony_ci	int i, err;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
109662306a36Sopenharmony_ci		return -EIO;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	data = devm_kzalloc(&client->dev, sizeof(struct asc7621_data),
109962306a36Sopenharmony_ci			    GFP_KERNEL);
110062306a36Sopenharmony_ci	if (data == NULL)
110162306a36Sopenharmony_ci		return -ENOMEM;
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	i2c_set_clientdata(client, data);
110462306a36Sopenharmony_ci	mutex_init(&data->update_lock);
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	/* Initialize the asc7621 chip */
110762306a36Sopenharmony_ci	asc7621_init_client(client);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	/* Create the sysfs entries */
111062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) {
111162306a36Sopenharmony_ci		err =
111262306a36Sopenharmony_ci		    device_create_file(&client->dev,
111362306a36Sopenharmony_ci				       &(asc7621_params[i].sda.dev_attr));
111462306a36Sopenharmony_ci		if (err)
111562306a36Sopenharmony_ci			goto exit_remove;
111662306a36Sopenharmony_ci	}
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	data->class_dev = hwmon_device_register(&client->dev);
111962306a36Sopenharmony_ci	if (IS_ERR(data->class_dev)) {
112062306a36Sopenharmony_ci		err = PTR_ERR(data->class_dev);
112162306a36Sopenharmony_ci		goto exit_remove;
112262306a36Sopenharmony_ci	}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	return 0;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ciexit_remove:
112762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) {
112862306a36Sopenharmony_ci		device_remove_file(&client->dev,
112962306a36Sopenharmony_ci				   &(asc7621_params[i].sda.dev_attr));
113062306a36Sopenharmony_ci	}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	return err;
113362306a36Sopenharmony_ci}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_cistatic int asc7621_detect(struct i2c_client *client,
113662306a36Sopenharmony_ci			  struct i2c_board_info *info)
113762306a36Sopenharmony_ci{
113862306a36Sopenharmony_ci	struct i2c_adapter *adapter = client->adapter;
113962306a36Sopenharmony_ci	int company, verstep, chip_index;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
114262306a36Sopenharmony_ci		return -ENODEV;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	for (chip_index = FIRST_CHIP; chip_index <= LAST_CHIP; chip_index++) {
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci		if (!valid_address_for_chip(chip_index, client->addr))
114762306a36Sopenharmony_ci			continue;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci		company = read_byte(client,
115062306a36Sopenharmony_ci			asc7621_chips[chip_index].company_reg);
115162306a36Sopenharmony_ci		verstep = read_byte(client,
115262306a36Sopenharmony_ci			asc7621_chips[chip_index].verstep_reg);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci		if (company == asc7621_chips[chip_index].company_id &&
115562306a36Sopenharmony_ci		    verstep == asc7621_chips[chip_index].verstep_id) {
115662306a36Sopenharmony_ci			strscpy(info->type, asc7621_chips[chip_index].name,
115762306a36Sopenharmony_ci				I2C_NAME_SIZE);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci			dev_info(&adapter->dev, "Matched %s at 0x%02x\n",
116062306a36Sopenharmony_ci				 asc7621_chips[chip_index].name, client->addr);
116162306a36Sopenharmony_ci			return 0;
116262306a36Sopenharmony_ci		}
116362306a36Sopenharmony_ci	}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	return -ENODEV;
116662306a36Sopenharmony_ci}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_cistatic void asc7621_remove(struct i2c_client *client)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	struct asc7621_data *data = i2c_get_clientdata(client);
117162306a36Sopenharmony_ci	int i;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	hwmon_device_unregister(data->class_dev);
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) {
117662306a36Sopenharmony_ci		device_remove_file(&client->dev,
117762306a36Sopenharmony_ci				   &(asc7621_params[i].sda.dev_attr));
117862306a36Sopenharmony_ci	}
117962306a36Sopenharmony_ci}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_cistatic const struct i2c_device_id asc7621_id[] = {
118262306a36Sopenharmony_ci	{"asc7621", asc7621},
118362306a36Sopenharmony_ci	{"asc7621a", asc7621a},
118462306a36Sopenharmony_ci	{},
118562306a36Sopenharmony_ci};
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, asc7621_id);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_cistatic struct i2c_driver asc7621_driver = {
119062306a36Sopenharmony_ci	.class = I2C_CLASS_HWMON,
119162306a36Sopenharmony_ci	.driver = {
119262306a36Sopenharmony_ci		.name = "asc7621",
119362306a36Sopenharmony_ci	},
119462306a36Sopenharmony_ci	.probe = asc7621_probe,
119562306a36Sopenharmony_ci	.remove = asc7621_remove,
119662306a36Sopenharmony_ci	.id_table = asc7621_id,
119762306a36Sopenharmony_ci	.detect = asc7621_detect,
119862306a36Sopenharmony_ci	.address_list = normal_i2c,
119962306a36Sopenharmony_ci};
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_cistatic int __init sm_asc7621_init(void)
120262306a36Sopenharmony_ci{
120362306a36Sopenharmony_ci	int i, j;
120462306a36Sopenharmony_ci/*
120562306a36Sopenharmony_ci * Collect all the registers needed into a single array.
120662306a36Sopenharmony_ci * This way, if a register isn't actually used for anything,
120762306a36Sopenharmony_ci * we don't retrieve it.
120862306a36Sopenharmony_ci */
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(asc7621_params); i++) {
121162306a36Sopenharmony_ci		for (j = 0; j < ARRAY_SIZE(asc7621_params[i].msb); j++)
121262306a36Sopenharmony_ci			asc7621_register_priorities[asc7621_params[i].msb[j]] =
121362306a36Sopenharmony_ci			    asc7621_params[i].priority;
121462306a36Sopenharmony_ci		for (j = 0; j < ARRAY_SIZE(asc7621_params[i].lsb); j++)
121562306a36Sopenharmony_ci			asc7621_register_priorities[asc7621_params[i].lsb[j]] =
121662306a36Sopenharmony_ci			    asc7621_params[i].priority;
121762306a36Sopenharmony_ci	}
121862306a36Sopenharmony_ci	return i2c_add_driver(&asc7621_driver);
121962306a36Sopenharmony_ci}
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_cistatic void __exit sm_asc7621_exit(void)
122262306a36Sopenharmony_ci{
122362306a36Sopenharmony_ci	i2c_del_driver(&asc7621_driver);
122462306a36Sopenharmony_ci}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
122762306a36Sopenharmony_ciMODULE_AUTHOR("George Joseph");
122862306a36Sopenharmony_ciMODULE_DESCRIPTION("Andigilog aSC7621 and aSC7621a driver");
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_cimodule_init(sm_asc7621_init);
123162306a36Sopenharmony_cimodule_exit(sm_asc7621_exit);
1232