162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * adt7x10.c - Part of lm_sensors, Linux kernel modules for hardware
462306a36Sopenharmony_ci *	 monitoring
562306a36Sopenharmony_ci * This driver handles the ADT7410 and compatible digital temperature sensors.
662306a36Sopenharmony_ci * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22
762306a36Sopenharmony_ci * based on lm75.c by Frodo Looijaard <frodol@dds.nl>
862306a36Sopenharmony_ci * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/device.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/init.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/jiffies.h>
1662306a36Sopenharmony_ci#include <linux/hwmon.h>
1762306a36Sopenharmony_ci#include <linux/err.h>
1862306a36Sopenharmony_ci#include <linux/mutex.h>
1962306a36Sopenharmony_ci#include <linux/delay.h>
2062306a36Sopenharmony_ci#include <linux/interrupt.h>
2162306a36Sopenharmony_ci#include <linux/regmap.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "adt7x10.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * ADT7X10 status
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_ci#define ADT7X10_STAT_T_LOW		(1 << 4)
2962306a36Sopenharmony_ci#define ADT7X10_STAT_T_HIGH		(1 << 5)
3062306a36Sopenharmony_ci#define ADT7X10_STAT_T_CRIT		(1 << 6)
3162306a36Sopenharmony_ci#define ADT7X10_STAT_NOT_RDY		(1 << 7)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/*
3462306a36Sopenharmony_ci * ADT7X10 config
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci#define ADT7X10_FAULT_QUEUE_MASK	(1 << 0 | 1 << 1)
3762306a36Sopenharmony_ci#define ADT7X10_CT_POLARITY		(1 << 2)
3862306a36Sopenharmony_ci#define ADT7X10_INT_POLARITY		(1 << 3)
3962306a36Sopenharmony_ci#define ADT7X10_EVENT_MODE		(1 << 4)
4062306a36Sopenharmony_ci#define ADT7X10_MODE_MASK		(1 << 5 | 1 << 6)
4162306a36Sopenharmony_ci#define ADT7X10_FULL			(0 << 5 | 0 << 6)
4262306a36Sopenharmony_ci#define ADT7X10_PD			(1 << 5 | 1 << 6)
4362306a36Sopenharmony_ci#define ADT7X10_RESOLUTION		(1 << 7)
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/*
4662306a36Sopenharmony_ci * ADT7X10 masks
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_ci#define ADT7X10_T13_VALUE_MASK		0xFFF8
4962306a36Sopenharmony_ci#define ADT7X10_T_HYST_MASK		0xF
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* straight from the datasheet */
5262306a36Sopenharmony_ci#define ADT7X10_TEMP_MIN (-55000)
5362306a36Sopenharmony_ci#define ADT7X10_TEMP_MAX 150000
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/* Each client has this additional data */
5662306a36Sopenharmony_cistruct adt7x10_data {
5762306a36Sopenharmony_ci	struct regmap		*regmap;
5862306a36Sopenharmony_ci	struct mutex		update_lock;
5962306a36Sopenharmony_ci	u8			config;
6062306a36Sopenharmony_ci	u8			oldconfig;
6162306a36Sopenharmony_ci	bool			valid;		/* true if temperature valid */
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cienum {
6562306a36Sopenharmony_ci	adt7x10_temperature = 0,
6662306a36Sopenharmony_ci	adt7x10_t_alarm_high,
6762306a36Sopenharmony_ci	adt7x10_t_alarm_low,
6862306a36Sopenharmony_ci	adt7x10_t_crit,
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic const u8 ADT7X10_REG_TEMP[] = {
7262306a36Sopenharmony_ci	[adt7x10_temperature] = ADT7X10_TEMPERATURE,		/* input */
7362306a36Sopenharmony_ci	[adt7x10_t_alarm_high] = ADT7X10_T_ALARM_HIGH,		/* high */
7462306a36Sopenharmony_ci	[adt7x10_t_alarm_low] = ADT7X10_T_ALARM_LOW,		/* low */
7562306a36Sopenharmony_ci	[adt7x10_t_crit] = ADT7X10_T_CRIT,			/* critical */
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic irqreturn_t adt7x10_irq_handler(int irq, void *private)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct device *dev = private;
8162306a36Sopenharmony_ci	struct adt7x10_data *d = dev_get_drvdata(dev);
8262306a36Sopenharmony_ci	unsigned int status;
8362306a36Sopenharmony_ci	int ret;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	ret = regmap_read(d->regmap, ADT7X10_STATUS, &status);
8662306a36Sopenharmony_ci	if (ret < 0)
8762306a36Sopenharmony_ci		return IRQ_HANDLED;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (status & ADT7X10_STAT_T_HIGH)
9062306a36Sopenharmony_ci		hwmon_notify_event(dev, hwmon_temp, hwmon_temp_max_alarm, 0);
9162306a36Sopenharmony_ci	if (status & ADT7X10_STAT_T_LOW)
9262306a36Sopenharmony_ci		hwmon_notify_event(dev, hwmon_temp, hwmon_temp_min_alarm, 0);
9362306a36Sopenharmony_ci	if (status & ADT7X10_STAT_T_CRIT)
9462306a36Sopenharmony_ci		hwmon_notify_event(dev, hwmon_temp, hwmon_temp_crit_alarm, 0);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	return IRQ_HANDLED;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic int adt7x10_temp_ready(struct regmap *regmap)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	unsigned int status;
10262306a36Sopenharmony_ci	int i, ret;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	for (i = 0; i < 6; i++) {
10562306a36Sopenharmony_ci		ret = regmap_read(regmap, ADT7X10_STATUS, &status);
10662306a36Sopenharmony_ci		if (ret < 0)
10762306a36Sopenharmony_ci			return ret;
10862306a36Sopenharmony_ci		if (!(status & ADT7X10_STAT_NOT_RDY))
10962306a36Sopenharmony_ci			return 0;
11062306a36Sopenharmony_ci		msleep(60);
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci	return -ETIMEDOUT;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic s16 ADT7X10_TEMP_TO_REG(long temp)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7X10_TEMP_MIN,
11862306a36Sopenharmony_ci					   ADT7X10_TEMP_MAX) * 128, 1000);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic int ADT7X10_REG_TO_TEMP(struct adt7x10_data *data, s16 reg)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	/* in 13 bit mode, bits 0-2 are status flags - mask them out */
12462306a36Sopenharmony_ci	if (!(data->config & ADT7X10_RESOLUTION))
12562306a36Sopenharmony_ci		reg &= ADT7X10_T13_VALUE_MASK;
12662306a36Sopenharmony_ci	/*
12762306a36Sopenharmony_ci	 * temperature is stored in twos complement format, in steps of
12862306a36Sopenharmony_ci	 * 1/128°C
12962306a36Sopenharmony_ci	 */
13062306a36Sopenharmony_ci	return DIV_ROUND_CLOSEST(reg * 1000, 128);
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci/*-----------------------------------------------------------------------*/
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic int adt7x10_temp_read(struct adt7x10_data *data, int index, long *val)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	unsigned int regval;
13862306a36Sopenharmony_ci	int ret;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
14162306a36Sopenharmony_ci	if (index == adt7x10_temperature && !data->valid) {
14262306a36Sopenharmony_ci		/* wait for valid temperature */
14362306a36Sopenharmony_ci		ret = adt7x10_temp_ready(data->regmap);
14462306a36Sopenharmony_ci		if (ret) {
14562306a36Sopenharmony_ci			mutex_unlock(&data->update_lock);
14662306a36Sopenharmony_ci			return ret;
14762306a36Sopenharmony_ci		}
14862306a36Sopenharmony_ci		data->valid = true;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	ret = regmap_read(data->regmap, ADT7X10_REG_TEMP[index], &regval);
15362306a36Sopenharmony_ci	if (ret)
15462306a36Sopenharmony_ci		return ret;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	*val = ADT7X10_REG_TO_TEMP(data, regval);
15762306a36Sopenharmony_ci	return 0;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic int adt7x10_temp_write(struct adt7x10_data *data, int index, long temp)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	int ret;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
16562306a36Sopenharmony_ci	ret = regmap_write(data->regmap, ADT7X10_REG_TEMP[index],
16662306a36Sopenharmony_ci			   ADT7X10_TEMP_TO_REG(temp));
16762306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
16862306a36Sopenharmony_ci	return ret;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic int adt7x10_hyst_read(struct adt7x10_data *data, int index, long *val)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	int hyst, temp, ret;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
17662306a36Sopenharmony_ci	ret = regmap_read(data->regmap, ADT7X10_T_HYST, &hyst);
17762306a36Sopenharmony_ci	if (ret) {
17862306a36Sopenharmony_ci		mutex_unlock(&data->update_lock);
17962306a36Sopenharmony_ci		return ret;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	ret = regmap_read(data->regmap, ADT7X10_REG_TEMP[index], &temp);
18362306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
18462306a36Sopenharmony_ci	if (ret)
18562306a36Sopenharmony_ci		return ret;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	hyst = (hyst & ADT7X10_T_HYST_MASK) * 1000;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/*
19062306a36Sopenharmony_ci	 * hysteresis is stored as a 4 bit offset in the device, convert it
19162306a36Sopenharmony_ci	 * to an absolute value
19262306a36Sopenharmony_ci	 */
19362306a36Sopenharmony_ci	/* min has positive offset, others have negative */
19462306a36Sopenharmony_ci	if (index == adt7x10_t_alarm_low)
19562306a36Sopenharmony_ci		hyst = -hyst;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	*val = ADT7X10_REG_TO_TEMP(data, temp) - hyst;
19862306a36Sopenharmony_ci	return 0;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic int adt7x10_hyst_write(struct adt7x10_data *data, long hyst)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	unsigned int regval;
20462306a36Sopenharmony_ci	int limit, ret;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* convert absolute hysteresis value to a 4 bit delta value */
20962306a36Sopenharmony_ci	ret = regmap_read(data->regmap, ADT7X10_T_ALARM_HIGH, &regval);
21062306a36Sopenharmony_ci	if (ret < 0)
21162306a36Sopenharmony_ci		goto abort;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	limit = ADT7X10_REG_TO_TEMP(data, regval);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	hyst = clamp_val(hyst, ADT7X10_TEMP_MIN, ADT7X10_TEMP_MAX);
21662306a36Sopenharmony_ci	regval = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000), 0,
21762306a36Sopenharmony_ci			   ADT7X10_T_HYST_MASK);
21862306a36Sopenharmony_ci	ret = regmap_write(data->regmap, ADT7X10_T_HYST, regval);
21962306a36Sopenharmony_ciabort:
22062306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
22162306a36Sopenharmony_ci	return ret;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic int adt7x10_alarm_read(struct adt7x10_data *data, int index, long *val)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	unsigned int status;
22762306a36Sopenharmony_ci	int ret;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	ret = regmap_read(data->regmap, ADT7X10_STATUS, &status);
23062306a36Sopenharmony_ci	if (ret < 0)
23162306a36Sopenharmony_ci		return ret;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	*val = !!(status & index);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	return 0;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic umode_t adt7x10_is_visible(const void *data,
23962306a36Sopenharmony_ci				  enum hwmon_sensor_types type,
24062306a36Sopenharmony_ci				  u32 attr, int channel)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	switch (attr) {
24362306a36Sopenharmony_ci	case hwmon_temp_max:
24462306a36Sopenharmony_ci	case hwmon_temp_min:
24562306a36Sopenharmony_ci	case hwmon_temp_crit:
24662306a36Sopenharmony_ci	case hwmon_temp_max_hyst:
24762306a36Sopenharmony_ci		return 0644;
24862306a36Sopenharmony_ci	case hwmon_temp_input:
24962306a36Sopenharmony_ci	case hwmon_temp_min_alarm:
25062306a36Sopenharmony_ci	case hwmon_temp_max_alarm:
25162306a36Sopenharmony_ci	case hwmon_temp_crit_alarm:
25262306a36Sopenharmony_ci	case hwmon_temp_min_hyst:
25362306a36Sopenharmony_ci	case hwmon_temp_crit_hyst:
25462306a36Sopenharmony_ci		return 0444;
25562306a36Sopenharmony_ci	default:
25662306a36Sopenharmony_ci		break;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	return 0;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic int adt7x10_read(struct device *dev, enum hwmon_sensor_types type,
26362306a36Sopenharmony_ci			u32 attr, int channel, long *val)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct adt7x10_data *data = dev_get_drvdata(dev);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	switch (attr) {
26862306a36Sopenharmony_ci	case hwmon_temp_input:
26962306a36Sopenharmony_ci		return adt7x10_temp_read(data, adt7x10_temperature, val);
27062306a36Sopenharmony_ci	case hwmon_temp_max:
27162306a36Sopenharmony_ci		return adt7x10_temp_read(data, adt7x10_t_alarm_high, val);
27262306a36Sopenharmony_ci	case hwmon_temp_min:
27362306a36Sopenharmony_ci		return adt7x10_temp_read(data, adt7x10_t_alarm_low, val);
27462306a36Sopenharmony_ci	case hwmon_temp_crit:
27562306a36Sopenharmony_ci		return adt7x10_temp_read(data, adt7x10_t_crit, val);
27662306a36Sopenharmony_ci	case hwmon_temp_max_hyst:
27762306a36Sopenharmony_ci		return adt7x10_hyst_read(data, adt7x10_t_alarm_high, val);
27862306a36Sopenharmony_ci	case hwmon_temp_min_hyst:
27962306a36Sopenharmony_ci		return adt7x10_hyst_read(data, adt7x10_t_alarm_low, val);
28062306a36Sopenharmony_ci	case hwmon_temp_crit_hyst:
28162306a36Sopenharmony_ci		return adt7x10_hyst_read(data, adt7x10_t_crit, val);
28262306a36Sopenharmony_ci	case hwmon_temp_min_alarm:
28362306a36Sopenharmony_ci		return adt7x10_alarm_read(data, ADT7X10_STAT_T_LOW, val);
28462306a36Sopenharmony_ci	case hwmon_temp_max_alarm:
28562306a36Sopenharmony_ci		return adt7x10_alarm_read(data, ADT7X10_STAT_T_HIGH, val);
28662306a36Sopenharmony_ci	case hwmon_temp_crit_alarm:
28762306a36Sopenharmony_ci		return adt7x10_alarm_read(data, ADT7X10_STAT_T_CRIT, val);
28862306a36Sopenharmony_ci	default:
28962306a36Sopenharmony_ci		return -EOPNOTSUPP;
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic int adt7x10_write(struct device *dev, enum hwmon_sensor_types type,
29462306a36Sopenharmony_ci			 u32 attr, int channel, long val)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	struct adt7x10_data *data = dev_get_drvdata(dev);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	switch (attr) {
29962306a36Sopenharmony_ci	case hwmon_temp_max:
30062306a36Sopenharmony_ci		return adt7x10_temp_write(data, adt7x10_t_alarm_high, val);
30162306a36Sopenharmony_ci	case hwmon_temp_min:
30262306a36Sopenharmony_ci		return adt7x10_temp_write(data, adt7x10_t_alarm_low, val);
30362306a36Sopenharmony_ci	case hwmon_temp_crit:
30462306a36Sopenharmony_ci		return adt7x10_temp_write(data, adt7x10_t_crit, val);
30562306a36Sopenharmony_ci	case hwmon_temp_max_hyst:
30662306a36Sopenharmony_ci		return adt7x10_hyst_write(data, val);
30762306a36Sopenharmony_ci	default:
30862306a36Sopenharmony_ci		return -EOPNOTSUPP;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic const struct hwmon_channel_info * const adt7x10_info[] = {
31362306a36Sopenharmony_ci	HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN |
31462306a36Sopenharmony_ci			   HWMON_T_CRIT | HWMON_T_MAX_HYST | HWMON_T_MIN_HYST |
31562306a36Sopenharmony_ci			   HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM |
31662306a36Sopenharmony_ci			   HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM),
31762306a36Sopenharmony_ci	NULL,
31862306a36Sopenharmony_ci};
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic const struct hwmon_ops adt7x10_hwmon_ops = {
32162306a36Sopenharmony_ci	.is_visible = adt7x10_is_visible,
32262306a36Sopenharmony_ci	.read = adt7x10_read,
32362306a36Sopenharmony_ci	.write = adt7x10_write,
32462306a36Sopenharmony_ci};
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic const struct hwmon_chip_info adt7x10_chip_info = {
32762306a36Sopenharmony_ci	.ops = &adt7x10_hwmon_ops,
32862306a36Sopenharmony_ci	.info = adt7x10_info,
32962306a36Sopenharmony_ci};
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic void adt7x10_restore_config(void *private)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct adt7x10_data *data = private;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	regmap_write(data->regmap, ADT7X10_CONFIG, data->oldconfig);
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ciint adt7x10_probe(struct device *dev, const char *name, int irq,
33962306a36Sopenharmony_ci		  struct regmap *regmap)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct adt7x10_data *data;
34262306a36Sopenharmony_ci	unsigned int config;
34362306a36Sopenharmony_ci	struct device *hdev;
34462306a36Sopenharmony_ci	int ret;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
34762306a36Sopenharmony_ci	if (!data)
34862306a36Sopenharmony_ci		return -ENOMEM;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	data->regmap = regmap;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	dev_set_drvdata(dev, data);
35362306a36Sopenharmony_ci	mutex_init(&data->update_lock);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/* configure as specified */
35662306a36Sopenharmony_ci	ret = regmap_read(regmap, ADT7X10_CONFIG, &config);
35762306a36Sopenharmony_ci	if (ret < 0) {
35862306a36Sopenharmony_ci		dev_dbg(dev, "Can't read config? %d\n", ret);
35962306a36Sopenharmony_ci		return ret;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci	data->oldconfig = config;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	/*
36462306a36Sopenharmony_ci	 * Set to 16 bit resolution, continous conversion and comparator mode.
36562306a36Sopenharmony_ci	 */
36662306a36Sopenharmony_ci	data->config = data->oldconfig;
36762306a36Sopenharmony_ci	data->config &= ~(ADT7X10_MODE_MASK | ADT7X10_CT_POLARITY |
36862306a36Sopenharmony_ci			ADT7X10_INT_POLARITY);
36962306a36Sopenharmony_ci	data->config |= ADT7X10_FULL | ADT7X10_RESOLUTION | ADT7X10_EVENT_MODE;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (data->config != data->oldconfig) {
37262306a36Sopenharmony_ci		ret = regmap_write(regmap, ADT7X10_CONFIG, data->config);
37362306a36Sopenharmony_ci		if (ret)
37462306a36Sopenharmony_ci			return ret;
37562306a36Sopenharmony_ci		ret = devm_add_action_or_reset(dev, adt7x10_restore_config, data);
37662306a36Sopenharmony_ci		if (ret)
37762306a36Sopenharmony_ci			return ret;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci	dev_dbg(dev, "Config %02x\n", data->config);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	hdev = devm_hwmon_device_register_with_info(dev, name, data,
38262306a36Sopenharmony_ci						    &adt7x10_chip_info, NULL);
38362306a36Sopenharmony_ci	if (IS_ERR(hdev))
38462306a36Sopenharmony_ci		return PTR_ERR(hdev);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (irq > 0) {
38762306a36Sopenharmony_ci		ret = devm_request_threaded_irq(dev, irq, NULL,
38862306a36Sopenharmony_ci						adt7x10_irq_handler,
38962306a36Sopenharmony_ci						IRQF_TRIGGER_FALLING |
39062306a36Sopenharmony_ci						IRQF_ONESHOT,
39162306a36Sopenharmony_ci						dev_name(dev), hdev);
39262306a36Sopenharmony_ci		if (ret)
39362306a36Sopenharmony_ci			return ret;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	return 0;
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(adt7x10_probe);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic int adt7x10_suspend(struct device *dev)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct adt7x10_data *data = dev_get_drvdata(dev);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	return regmap_write(data->regmap, ADT7X10_CONFIG,
40562306a36Sopenharmony_ci			    data->config | ADT7X10_PD);
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic int adt7x10_resume(struct device *dev)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	struct adt7x10_data *data = dev_get_drvdata(dev);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	return regmap_write(data->regmap, ADT7X10_CONFIG, data->config);
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ciEXPORT_SIMPLE_DEV_PM_OPS(adt7x10_dev_pm_ops, adt7x10_suspend, adt7x10_resume);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ciMODULE_AUTHOR("Hartmut Knaack");
41862306a36Sopenharmony_ciMODULE_DESCRIPTION("ADT7410/ADT7420, ADT7310/ADT7320 common code");
41962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
420