162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/* Driver for the Texas Instruments TMP464 SMBus temperature sensor IC.
462306a36Sopenharmony_ci * Supported models: TMP464, TMP468
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci * Copyright (C) 2022 Agathe Porte <agathe.porte@nokia.com>
762306a36Sopenharmony_ci * Preliminary support by:
862306a36Sopenharmony_ci * Lionel Pouliquen <lionel.lp.pouliquen@nokia.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/hwmon.h>
1362306a36Sopenharmony_ci#include <linux/i2c.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/mutex.h>
1762306a36Sopenharmony_ci#include <linux/of.h>
1862306a36Sopenharmony_ci#include <linux/regmap.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/* Addresses to scan */
2262306a36Sopenharmony_cistatic const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, I2C_CLIENT_END };
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define TMP464_NUM_CHANNELS		5	/* chan 0 is internal, 1-4 are remote */
2562306a36Sopenharmony_ci#define TMP468_NUM_CHANNELS		9	/* chan 0 is internal, 1-8 are remote */
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define MAX_CHANNELS			9
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define TMP464_TEMP_REG(channel)	(channel)
3062306a36Sopenharmony_ci#define TMP464_TEMP_OFFSET_REG(channel)	(0x40 + ((channel) - 1) * 8)
3162306a36Sopenharmony_ci#define TMP464_N_FACTOR_REG(channel)	(0x41 + ((channel) - 1) * 8)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic const u8 TMP464_THERM_LIMIT[MAX_CHANNELS] = {
3462306a36Sopenharmony_ci	0x39, 0x42, 0x4A, 0x52, 0x5A, 0x62, 0x6a, 0x72, 0x7a };
3562306a36Sopenharmony_cistatic const u8 TMP464_THERM2_LIMIT[MAX_CHANNELS] = {
3662306a36Sopenharmony_ci	0x3A, 0x43, 0x4B, 0x53, 0x5B, 0x63, 0x6b, 0x73, 0x7b };
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define TMP464_THERM_STATUS_REG			0x21
3962306a36Sopenharmony_ci#define TMP464_THERM2_STATUS_REG		0x22
4062306a36Sopenharmony_ci#define TMP464_REMOTE_OPEN_REG			0x23
4162306a36Sopenharmony_ci#define TMP464_CONFIG_REG			0x30
4262306a36Sopenharmony_ci#define TMP464_TEMP_HYST_REG			0x38
4362306a36Sopenharmony_ci#define TMP464_LOCK_REG				0xc4
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* Identification */
4662306a36Sopenharmony_ci#define TMP464_MANUFACTURER_ID_REG		0xFE
4762306a36Sopenharmony_ci#define TMP464_DEVICE_ID_REG			0xFF
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* Flags */
5062306a36Sopenharmony_ci#define TMP464_CONFIG_SHUTDOWN			BIT(5)
5162306a36Sopenharmony_ci#define TMP464_CONFIG_RANGE			0x04
5262306a36Sopenharmony_ci#define TMP464_CONFIG_REG_REN(x)		(BIT(7 + (x)))
5362306a36Sopenharmony_ci#define TMP464_CONFIG_REG_REN_MASK		GENMASK(15, 7)
5462306a36Sopenharmony_ci#define TMP464_CONFIG_CONVERSION_RATE_B0	2
5562306a36Sopenharmony_ci#define TMP464_CONFIG_CONVERSION_RATE_B2	4
5662306a36Sopenharmony_ci#define TMP464_CONFIG_CONVERSION_RATE_MASK      GENMASK(TMP464_CONFIG_CONVERSION_RATE_B2, \
5762306a36Sopenharmony_ci							TMP464_CONFIG_CONVERSION_RATE_B0)
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define TMP464_UNLOCK_VAL			0xeb19
6062306a36Sopenharmony_ci#define TMP464_LOCK_VAL				0x5ca6
6162306a36Sopenharmony_ci#define TMP464_LOCKED				0x8000
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* Manufacturer / Device ID's */
6462306a36Sopenharmony_ci#define TMP464_MANUFACTURER_ID			0x5449
6562306a36Sopenharmony_ci#define TMP464_DEVICE_ID			0x1468
6662306a36Sopenharmony_ci#define TMP468_DEVICE_ID			0x0468
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic const struct i2c_device_id tmp464_id[] = {
6962306a36Sopenharmony_ci	{ "tmp464", TMP464_NUM_CHANNELS },
7062306a36Sopenharmony_ci	{ "tmp468", TMP468_NUM_CHANNELS },
7162306a36Sopenharmony_ci	{ }
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tmp464_id);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic const struct of_device_id __maybe_unused tmp464_of_match[] = {
7662306a36Sopenharmony_ci	{
7762306a36Sopenharmony_ci		.compatible = "ti,tmp464",
7862306a36Sopenharmony_ci		.data = (void *)TMP464_NUM_CHANNELS
7962306a36Sopenharmony_ci	},
8062306a36Sopenharmony_ci	{
8162306a36Sopenharmony_ci		.compatible = "ti,tmp468",
8262306a36Sopenharmony_ci		.data = (void *)TMP468_NUM_CHANNELS
8362306a36Sopenharmony_ci	},
8462306a36Sopenharmony_ci	{},
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tmp464_of_match);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistruct tmp464_channel {
8962306a36Sopenharmony_ci	const char *label;
9062306a36Sopenharmony_ci	bool enabled;
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistruct tmp464_data {
9462306a36Sopenharmony_ci	struct regmap *regmap;
9562306a36Sopenharmony_ci	struct mutex update_lock;
9662306a36Sopenharmony_ci	int channels;
9762306a36Sopenharmony_ci	s16 config_orig;
9862306a36Sopenharmony_ci	u16 open_reg;
9962306a36Sopenharmony_ci	unsigned long last_updated;
10062306a36Sopenharmony_ci	bool valid;
10162306a36Sopenharmony_ci	int update_interval;
10262306a36Sopenharmony_ci	struct tmp464_channel channel[MAX_CHANNELS];
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic int temp_from_reg(s16 reg)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	return DIV_ROUND_CLOSEST((reg >> 3) * 625, 10);
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic s16 temp_to_limit_reg(long temp)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	return DIV_ROUND_CLOSEST(temp, 500) << 6;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic s16 temp_to_offset_reg(long temp)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	return DIV_ROUND_CLOSEST(temp * 10, 625) << 3;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int tmp464_enable_channels(struct tmp464_data *data)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct regmap *regmap = data->regmap;
12362306a36Sopenharmony_ci	u16 enable = 0;
12462306a36Sopenharmony_ci	int i;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	for (i = 0; i < data->channels; i++)
12762306a36Sopenharmony_ci		if (data->channel[i].enabled)
12862306a36Sopenharmony_ci			enable |= TMP464_CONFIG_REG_REN(i);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return regmap_update_bits(regmap, TMP464_CONFIG_REG, TMP464_CONFIG_REG_REN_MASK, enable);
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic int tmp464_chip_read(struct device *dev, u32 attr, int channel, long *val)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct tmp464_data *data = dev_get_drvdata(dev);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	switch (attr) {
13862306a36Sopenharmony_ci	case hwmon_chip_update_interval:
13962306a36Sopenharmony_ci		*val = data->update_interval;
14062306a36Sopenharmony_ci		return 0;
14162306a36Sopenharmony_ci	default:
14262306a36Sopenharmony_ci		return -EOPNOTSUPP;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic int tmp464_temp_read(struct device *dev, u32 attr, int channel, long *val)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct tmp464_data *data = dev_get_drvdata(dev);
14962306a36Sopenharmony_ci	struct regmap *regmap = data->regmap;
15062306a36Sopenharmony_ci	unsigned int regval, regval2;
15162306a36Sopenharmony_ci	int err = 0;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	switch (attr) {
15662306a36Sopenharmony_ci	case hwmon_temp_max_alarm:
15762306a36Sopenharmony_ci		err = regmap_read(regmap, TMP464_THERM_STATUS_REG, &regval);
15862306a36Sopenharmony_ci		if (err < 0)
15962306a36Sopenharmony_ci			break;
16062306a36Sopenharmony_ci		*val = !!(regval & BIT(channel + 7));
16162306a36Sopenharmony_ci		break;
16262306a36Sopenharmony_ci	case hwmon_temp_crit_alarm:
16362306a36Sopenharmony_ci		err = regmap_read(regmap, TMP464_THERM2_STATUS_REG, &regval);
16462306a36Sopenharmony_ci		if (err < 0)
16562306a36Sopenharmony_ci			break;
16662306a36Sopenharmony_ci		*val = !!(regval & BIT(channel + 7));
16762306a36Sopenharmony_ci		break;
16862306a36Sopenharmony_ci	case hwmon_temp_fault:
16962306a36Sopenharmony_ci		/*
17062306a36Sopenharmony_ci		 * The chip clears TMP464_REMOTE_OPEN_REG after it is read
17162306a36Sopenharmony_ci		 * and only updates it after the next measurement cycle is
17262306a36Sopenharmony_ci		 * complete. That means we have to cache the value internally
17362306a36Sopenharmony_ci		 * for one measurement cycle and report the cached value.
17462306a36Sopenharmony_ci		 */
17562306a36Sopenharmony_ci		if (!data->valid || time_after(jiffies, data->last_updated +
17662306a36Sopenharmony_ci					       msecs_to_jiffies(data->update_interval))) {
17762306a36Sopenharmony_ci			err = regmap_read(regmap, TMP464_REMOTE_OPEN_REG, &regval);
17862306a36Sopenharmony_ci			if (err < 0)
17962306a36Sopenharmony_ci				break;
18062306a36Sopenharmony_ci			data->open_reg = regval;
18162306a36Sopenharmony_ci			data->last_updated = jiffies;
18262306a36Sopenharmony_ci			data->valid = true;
18362306a36Sopenharmony_ci		}
18462306a36Sopenharmony_ci		*val = !!(data->open_reg & BIT(channel + 7));
18562306a36Sopenharmony_ci		break;
18662306a36Sopenharmony_ci	case hwmon_temp_max_hyst:
18762306a36Sopenharmony_ci		err = regmap_read(regmap, TMP464_THERM_LIMIT[channel], &regval);
18862306a36Sopenharmony_ci		if (err < 0)
18962306a36Sopenharmony_ci			break;
19062306a36Sopenharmony_ci		err = regmap_read(regmap, TMP464_TEMP_HYST_REG, &regval2);
19162306a36Sopenharmony_ci		if (err < 0)
19262306a36Sopenharmony_ci			break;
19362306a36Sopenharmony_ci		regval -= regval2;
19462306a36Sopenharmony_ci		*val = temp_from_reg(regval);
19562306a36Sopenharmony_ci		break;
19662306a36Sopenharmony_ci	case hwmon_temp_max:
19762306a36Sopenharmony_ci		err = regmap_read(regmap, TMP464_THERM_LIMIT[channel], &regval);
19862306a36Sopenharmony_ci		if (err < 0)
19962306a36Sopenharmony_ci			break;
20062306a36Sopenharmony_ci		*val = temp_from_reg(regval);
20162306a36Sopenharmony_ci		break;
20262306a36Sopenharmony_ci	case hwmon_temp_crit_hyst:
20362306a36Sopenharmony_ci		err = regmap_read(regmap, TMP464_THERM2_LIMIT[channel], &regval);
20462306a36Sopenharmony_ci		if (err < 0)
20562306a36Sopenharmony_ci			break;
20662306a36Sopenharmony_ci		err = regmap_read(regmap, TMP464_TEMP_HYST_REG, &regval2);
20762306a36Sopenharmony_ci		if (err < 0)
20862306a36Sopenharmony_ci			break;
20962306a36Sopenharmony_ci		regval -= regval2;
21062306a36Sopenharmony_ci		*val = temp_from_reg(regval);
21162306a36Sopenharmony_ci		break;
21262306a36Sopenharmony_ci	case hwmon_temp_crit:
21362306a36Sopenharmony_ci		err = regmap_read(regmap, TMP464_THERM2_LIMIT[channel], &regval);
21462306a36Sopenharmony_ci		if (err < 0)
21562306a36Sopenharmony_ci			break;
21662306a36Sopenharmony_ci		*val = temp_from_reg(regval);
21762306a36Sopenharmony_ci		break;
21862306a36Sopenharmony_ci	case hwmon_temp_offset:
21962306a36Sopenharmony_ci		err = regmap_read(regmap, TMP464_TEMP_OFFSET_REG(channel), &regval);
22062306a36Sopenharmony_ci		if (err < 0)
22162306a36Sopenharmony_ci			break;
22262306a36Sopenharmony_ci		*val = temp_from_reg(regval);
22362306a36Sopenharmony_ci		break;
22462306a36Sopenharmony_ci	case hwmon_temp_input:
22562306a36Sopenharmony_ci		if (!data->channel[channel].enabled) {
22662306a36Sopenharmony_ci			err = -ENODATA;
22762306a36Sopenharmony_ci			break;
22862306a36Sopenharmony_ci		}
22962306a36Sopenharmony_ci		err = regmap_read(regmap, TMP464_TEMP_REG(channel), &regval);
23062306a36Sopenharmony_ci		if (err < 0)
23162306a36Sopenharmony_ci			break;
23262306a36Sopenharmony_ci		*val = temp_from_reg(regval);
23362306a36Sopenharmony_ci		break;
23462306a36Sopenharmony_ci	case hwmon_temp_enable:
23562306a36Sopenharmony_ci		*val = data->channel[channel].enabled;
23662306a36Sopenharmony_ci		break;
23762306a36Sopenharmony_ci	default:
23862306a36Sopenharmony_ci		err = -EOPNOTSUPP;
23962306a36Sopenharmony_ci		break;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	return err;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic int tmp464_read(struct device *dev, enum hwmon_sensor_types type,
24862306a36Sopenharmony_ci		       u32 attr, int channel, long *val)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	switch (type) {
25162306a36Sopenharmony_ci	case hwmon_chip:
25262306a36Sopenharmony_ci		return tmp464_chip_read(dev, attr, channel, val);
25362306a36Sopenharmony_ci	case hwmon_temp:
25462306a36Sopenharmony_ci		return tmp464_temp_read(dev, attr, channel, val);
25562306a36Sopenharmony_ci	default:
25662306a36Sopenharmony_ci		return -EOPNOTSUPP;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int tmp464_read_string(struct device *dev, enum hwmon_sensor_types type,
26162306a36Sopenharmony_ci			      u32 attr, int channel, const char **str)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct tmp464_data *data = dev_get_drvdata(dev);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	*str = data->channel[channel].label;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	return 0;
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic int tmp464_set_convrate(struct tmp464_data *data, long interval)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	int rate;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/*
27562306a36Sopenharmony_ci	 * For valid rates, interval in milli-seconds can be calculated as
27662306a36Sopenharmony_ci	 *      interval = 125 << (7 - rate);
27762306a36Sopenharmony_ci	 * or
27862306a36Sopenharmony_ci	 *      interval = (1 << (7 - rate)) * 125;
27962306a36Sopenharmony_ci	 * The rate is therefore
28062306a36Sopenharmony_ci	 *      rate = 7 - __fls(interval / 125);
28162306a36Sopenharmony_ci	 * and the rounded rate is
28262306a36Sopenharmony_ci	 *      rate = 7 - __fls(interval * 4 / (125 * 3));
28362306a36Sopenharmony_ci	 * Use clamp_val() to avoid overflows, and to ensure valid input
28462306a36Sopenharmony_ci	 * for __fls.
28562306a36Sopenharmony_ci	 */
28662306a36Sopenharmony_ci	interval = clamp_val(interval, 125, 16000);
28762306a36Sopenharmony_ci	rate = 7 - __fls(interval * 4 / (125 * 3));
28862306a36Sopenharmony_ci	data->update_interval = 125 << (7 - rate);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return regmap_update_bits(data->regmap, TMP464_CONFIG_REG,
29162306a36Sopenharmony_ci				  TMP464_CONFIG_CONVERSION_RATE_MASK,
29262306a36Sopenharmony_ci				  rate << TMP464_CONFIG_CONVERSION_RATE_B0);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic int tmp464_chip_write(struct tmp464_data *data, u32 attr, int channel, long val)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	switch (attr) {
29862306a36Sopenharmony_ci	case hwmon_chip_update_interval:
29962306a36Sopenharmony_ci		return tmp464_set_convrate(data, val);
30062306a36Sopenharmony_ci	default:
30162306a36Sopenharmony_ci		return -EOPNOTSUPP;
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic int tmp464_temp_write(struct tmp464_data *data, u32 attr, int channel, long val)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct regmap *regmap = data->regmap;
30862306a36Sopenharmony_ci	unsigned int regval;
30962306a36Sopenharmony_ci	int err = 0;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	switch (attr) {
31262306a36Sopenharmony_ci	case hwmon_temp_max_hyst:
31362306a36Sopenharmony_ci		err = regmap_read(regmap, TMP464_THERM_LIMIT[0], &regval);
31462306a36Sopenharmony_ci		if (err < 0)
31562306a36Sopenharmony_ci			break;
31662306a36Sopenharmony_ci		val = clamp_val(val, -256000, 256000);	/* prevent overflow/underflow */
31762306a36Sopenharmony_ci		val = clamp_val(temp_from_reg(regval) - val, 0, 255000);
31862306a36Sopenharmony_ci		err = regmap_write(regmap, TMP464_TEMP_HYST_REG,
31962306a36Sopenharmony_ci				   DIV_ROUND_CLOSEST(val, 1000) << 7);
32062306a36Sopenharmony_ci		break;
32162306a36Sopenharmony_ci	case hwmon_temp_max:
32262306a36Sopenharmony_ci		val = temp_to_limit_reg(clamp_val(val, -255000, 255500));
32362306a36Sopenharmony_ci		err = regmap_write(regmap, TMP464_THERM_LIMIT[channel], val);
32462306a36Sopenharmony_ci		break;
32562306a36Sopenharmony_ci	case hwmon_temp_crit:
32662306a36Sopenharmony_ci		val = temp_to_limit_reg(clamp_val(val, -255000, 255500));
32762306a36Sopenharmony_ci		err = regmap_write(regmap, TMP464_THERM2_LIMIT[channel], val);
32862306a36Sopenharmony_ci		break;
32962306a36Sopenharmony_ci	case hwmon_temp_offset:
33062306a36Sopenharmony_ci		val = temp_to_offset_reg(clamp_val(val, -128000, 127937));
33162306a36Sopenharmony_ci		err = regmap_write(regmap, TMP464_TEMP_OFFSET_REG(channel), val);
33262306a36Sopenharmony_ci		break;
33362306a36Sopenharmony_ci	case hwmon_temp_enable:
33462306a36Sopenharmony_ci		data->channel[channel].enabled = !!val;
33562306a36Sopenharmony_ci		err = tmp464_enable_channels(data);
33662306a36Sopenharmony_ci		break;
33762306a36Sopenharmony_ci	default:
33862306a36Sopenharmony_ci		err = -EOPNOTSUPP;
33962306a36Sopenharmony_ci		break;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	return err;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic int tmp464_write(struct device *dev, enum hwmon_sensor_types type,
34662306a36Sopenharmony_ci			u32 attr, int channel, long val)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	struct tmp464_data *data = dev_get_drvdata(dev);
34962306a36Sopenharmony_ci	int err;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	mutex_lock(&data->update_lock);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	switch (type) {
35462306a36Sopenharmony_ci	case hwmon_chip:
35562306a36Sopenharmony_ci		err = tmp464_chip_write(data, attr, channel, val);
35662306a36Sopenharmony_ci		break;
35762306a36Sopenharmony_ci	case hwmon_temp:
35862306a36Sopenharmony_ci		err = tmp464_temp_write(data, attr, channel, val);
35962306a36Sopenharmony_ci		break;
36062306a36Sopenharmony_ci	default:
36162306a36Sopenharmony_ci		err = -EOPNOTSUPP;
36262306a36Sopenharmony_ci		break;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	mutex_unlock(&data->update_lock);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	return err;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic umode_t tmp464_is_visible(const void *_data, enum hwmon_sensor_types type,
37162306a36Sopenharmony_ci				 u32 attr, int channel)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	const struct tmp464_data *data = _data;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (channel >= data->channels)
37662306a36Sopenharmony_ci		return 0;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (type == hwmon_chip) {
37962306a36Sopenharmony_ci		if (attr == hwmon_chip_update_interval)
38062306a36Sopenharmony_ci			return 0644;
38162306a36Sopenharmony_ci		return 0;
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	switch (attr) {
38562306a36Sopenharmony_ci	case hwmon_temp_input:
38662306a36Sopenharmony_ci	case hwmon_temp_max_alarm:
38762306a36Sopenharmony_ci	case hwmon_temp_crit_alarm:
38862306a36Sopenharmony_ci	case hwmon_temp_crit_hyst:
38962306a36Sopenharmony_ci		return 0444;
39062306a36Sopenharmony_ci	case hwmon_temp_enable:
39162306a36Sopenharmony_ci	case hwmon_temp_max:
39262306a36Sopenharmony_ci	case hwmon_temp_crit:
39362306a36Sopenharmony_ci		return 0644;
39462306a36Sopenharmony_ci	case hwmon_temp_max_hyst:
39562306a36Sopenharmony_ci		if (!channel)
39662306a36Sopenharmony_ci			return 0644;
39762306a36Sopenharmony_ci		return 0444;
39862306a36Sopenharmony_ci	case hwmon_temp_label:
39962306a36Sopenharmony_ci		if (data->channel[channel].label)
40062306a36Sopenharmony_ci			return 0444;
40162306a36Sopenharmony_ci		return 0;
40262306a36Sopenharmony_ci	case hwmon_temp_fault:
40362306a36Sopenharmony_ci		if (channel)
40462306a36Sopenharmony_ci			return 0444;
40562306a36Sopenharmony_ci		return 0;
40662306a36Sopenharmony_ci	case hwmon_temp_offset:
40762306a36Sopenharmony_ci		if (channel)
40862306a36Sopenharmony_ci			return 0644;
40962306a36Sopenharmony_ci		return 0;
41062306a36Sopenharmony_ci	default:
41162306a36Sopenharmony_ci		return 0;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic void tmp464_restore_lock(void *regmap)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	regmap_write(regmap, TMP464_LOCK_REG, TMP464_LOCK_VAL);
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic void tmp464_restore_config(void *_data)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	struct tmp464_data *data = _data;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	regmap_write(data->regmap, TMP464_CONFIG_REG, data->config_orig);
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic int tmp464_init_client(struct device *dev, struct tmp464_data *data)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	struct regmap *regmap = data->regmap;
43062306a36Sopenharmony_ci	unsigned int regval;
43162306a36Sopenharmony_ci	int err;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	err = regmap_read(regmap, TMP464_LOCK_REG, &regval);
43462306a36Sopenharmony_ci	if (err)
43562306a36Sopenharmony_ci		return err;
43662306a36Sopenharmony_ci	if (regval == TMP464_LOCKED) {
43762306a36Sopenharmony_ci		/* Explicitly unlock chip if it is locked */
43862306a36Sopenharmony_ci		err = regmap_write(regmap, TMP464_LOCK_REG, TMP464_UNLOCK_VAL);
43962306a36Sopenharmony_ci		if (err)
44062306a36Sopenharmony_ci			return err;
44162306a36Sopenharmony_ci		/* and lock it again when unloading the driver */
44262306a36Sopenharmony_ci		err = devm_add_action_or_reset(dev, tmp464_restore_lock, regmap);
44362306a36Sopenharmony_ci		if (err)
44462306a36Sopenharmony_ci			return err;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	err = regmap_read(regmap, TMP464_CONFIG_REG, &regval);
44862306a36Sopenharmony_ci	if (err)
44962306a36Sopenharmony_ci		return err;
45062306a36Sopenharmony_ci	data->config_orig = regval;
45162306a36Sopenharmony_ci	err = devm_add_action_or_reset(dev, tmp464_restore_config, data);
45262306a36Sopenharmony_ci	if (err)
45362306a36Sopenharmony_ci		return err;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	/* Default to 500 ms update interval */
45662306a36Sopenharmony_ci	err = regmap_update_bits(regmap, TMP464_CONFIG_REG,
45762306a36Sopenharmony_ci				 TMP464_CONFIG_CONVERSION_RATE_MASK | TMP464_CONFIG_SHUTDOWN,
45862306a36Sopenharmony_ci				 BIT(TMP464_CONFIG_CONVERSION_RATE_B0) |
45962306a36Sopenharmony_ci				 BIT(TMP464_CONFIG_CONVERSION_RATE_B2));
46062306a36Sopenharmony_ci	if (err)
46162306a36Sopenharmony_ci		return err;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	data->update_interval = 500;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	return tmp464_enable_channels(data);
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_cistatic int tmp464_detect(struct i2c_client *client,
46962306a36Sopenharmony_ci			 struct i2c_board_info *info)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	struct i2c_adapter *adapter = client->adapter;
47262306a36Sopenharmony_ci	char *name, *chip;
47362306a36Sopenharmony_ci	int reg;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
47662306a36Sopenharmony_ci		return -ENODEV;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	reg = i2c_smbus_read_word_swapped(client, TMP464_MANUFACTURER_ID_REG);
47962306a36Sopenharmony_ci	if (reg < 0)
48062306a36Sopenharmony_ci		return reg;
48162306a36Sopenharmony_ci	if (reg != TMP464_MANUFACTURER_ID)
48262306a36Sopenharmony_ci		return -ENODEV;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	/* Check for "always return zero" bits */
48562306a36Sopenharmony_ci	reg = i2c_smbus_read_word_swapped(client, TMP464_THERM_STATUS_REG);
48662306a36Sopenharmony_ci	if (reg < 0)
48762306a36Sopenharmony_ci		return reg;
48862306a36Sopenharmony_ci	if (reg & 0x1f)
48962306a36Sopenharmony_ci		return -ENODEV;
49062306a36Sopenharmony_ci	reg = i2c_smbus_read_word_swapped(client, TMP464_THERM2_STATUS_REG);
49162306a36Sopenharmony_ci	if (reg < 0)
49262306a36Sopenharmony_ci		return reg;
49362306a36Sopenharmony_ci	if (reg & 0x1f)
49462306a36Sopenharmony_ci		return -ENODEV;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	reg = i2c_smbus_read_word_swapped(client, TMP464_DEVICE_ID_REG);
49762306a36Sopenharmony_ci	if (reg < 0)
49862306a36Sopenharmony_ci		return reg;
49962306a36Sopenharmony_ci	switch (reg) {
50062306a36Sopenharmony_ci	case TMP464_DEVICE_ID:
50162306a36Sopenharmony_ci		name = "tmp464";
50262306a36Sopenharmony_ci		chip = "TMP464";
50362306a36Sopenharmony_ci		break;
50462306a36Sopenharmony_ci	case TMP468_DEVICE_ID:
50562306a36Sopenharmony_ci		name = "tmp468";
50662306a36Sopenharmony_ci		chip = "TMP468";
50762306a36Sopenharmony_ci		break;
50862306a36Sopenharmony_ci	default:
50962306a36Sopenharmony_ci		return -ENODEV;
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	strscpy(info->type, name, I2C_NAME_SIZE);
51362306a36Sopenharmony_ci	dev_info(&adapter->dev, "Detected TI %s chip at 0x%02x\n", chip, client->addr);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	return 0;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cistatic int tmp464_probe_child_from_dt(struct device *dev,
51962306a36Sopenharmony_ci				      struct device_node *child,
52062306a36Sopenharmony_ci				      struct tmp464_data *data)
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	struct regmap *regmap = data->regmap;
52462306a36Sopenharmony_ci	u32 channel;
52562306a36Sopenharmony_ci	s32 nfactor;
52662306a36Sopenharmony_ci	int err;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	err = of_property_read_u32(child, "reg", &channel);
52962306a36Sopenharmony_ci	if (err) {
53062306a36Sopenharmony_ci		dev_err(dev, "missing reg property of %pOFn\n", child);
53162306a36Sopenharmony_ci		return err;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	if (channel >= data->channels) {
53562306a36Sopenharmony_ci		dev_err(dev, "invalid reg %d of %pOFn\n", channel, child);
53662306a36Sopenharmony_ci		return -EINVAL;
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	of_property_read_string(child, "label", &data->channel[channel].label);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	data->channel[channel].enabled = of_device_is_available(child);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	err = of_property_read_s32(child, "ti,n-factor", &nfactor);
54462306a36Sopenharmony_ci	if (err && err != -EINVAL)
54562306a36Sopenharmony_ci		return err;
54662306a36Sopenharmony_ci	if (!err) {
54762306a36Sopenharmony_ci		if (channel == 0) {
54862306a36Sopenharmony_ci			dev_err(dev, "n-factor can't be set for internal channel\n");
54962306a36Sopenharmony_ci			return -EINVAL;
55062306a36Sopenharmony_ci		}
55162306a36Sopenharmony_ci		if (nfactor > 127 || nfactor < -128) {
55262306a36Sopenharmony_ci			dev_err(dev, "n-factor for channel %d invalid (%d)\n",
55362306a36Sopenharmony_ci				channel, nfactor);
55462306a36Sopenharmony_ci			return -EINVAL;
55562306a36Sopenharmony_ci		}
55662306a36Sopenharmony_ci		err = regmap_write(regmap, TMP464_N_FACTOR_REG(channel),
55762306a36Sopenharmony_ci				   (nfactor << 8) & 0xff00);
55862306a36Sopenharmony_ci		if (err)
55962306a36Sopenharmony_ci			return err;
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	return 0;
56362306a36Sopenharmony_ci}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_cistatic int tmp464_probe_from_dt(struct device *dev, struct tmp464_data *data)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	const struct device_node *np = dev->of_node;
56862306a36Sopenharmony_ci	struct device_node *child;
56962306a36Sopenharmony_ci	int err;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	for_each_child_of_node(np, child) {
57262306a36Sopenharmony_ci		if (strcmp(child->name, "channel"))
57362306a36Sopenharmony_ci			continue;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci		err = tmp464_probe_child_from_dt(dev, child, data);
57662306a36Sopenharmony_ci		if (err) {
57762306a36Sopenharmony_ci			of_node_put(child);
57862306a36Sopenharmony_ci			return err;
57962306a36Sopenharmony_ci		}
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	return 0;
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic const struct hwmon_ops tmp464_ops = {
58662306a36Sopenharmony_ci	.is_visible = tmp464_is_visible,
58762306a36Sopenharmony_ci	.read = tmp464_read,
58862306a36Sopenharmony_ci	.read_string = tmp464_read_string,
58962306a36Sopenharmony_ci	.write = tmp464_write,
59062306a36Sopenharmony_ci};
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_cistatic const struct hwmon_channel_info * const tmp464_info[] = {
59362306a36Sopenharmony_ci	HWMON_CHANNEL_INFO(chip,
59462306a36Sopenharmony_ci			   HWMON_C_UPDATE_INTERVAL),
59562306a36Sopenharmony_ci	HWMON_CHANNEL_INFO(temp,
59662306a36Sopenharmony_ci			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_CRIT |
59762306a36Sopenharmony_ci			   HWMON_T_CRIT_HYST | HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM |
59862306a36Sopenharmony_ci			   HWMON_T_LABEL | HWMON_T_ENABLE,
59962306a36Sopenharmony_ci			   HWMON_T_INPUT | HWMON_T_OFFSET | HWMON_T_MAX | HWMON_T_MAX_HYST |
60062306a36Sopenharmony_ci			   HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MAX_ALARM |
60162306a36Sopenharmony_ci			   HWMON_T_CRIT_ALARM | HWMON_T_FAULT | HWMON_T_LABEL | HWMON_T_ENABLE,
60262306a36Sopenharmony_ci			   HWMON_T_INPUT | HWMON_T_OFFSET | HWMON_T_MAX | HWMON_T_MAX_HYST |
60362306a36Sopenharmony_ci			   HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MAX_ALARM |
60462306a36Sopenharmony_ci			   HWMON_T_CRIT_ALARM | HWMON_T_FAULT | HWMON_T_LABEL | HWMON_T_ENABLE,
60562306a36Sopenharmony_ci			   HWMON_T_INPUT | HWMON_T_OFFSET | HWMON_T_MAX | HWMON_T_MAX_HYST |
60662306a36Sopenharmony_ci			   HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MAX_ALARM |
60762306a36Sopenharmony_ci			   HWMON_T_CRIT_ALARM | HWMON_T_FAULT | HWMON_T_LABEL | HWMON_T_ENABLE,
60862306a36Sopenharmony_ci			   HWMON_T_INPUT | HWMON_T_OFFSET | HWMON_T_MAX | HWMON_T_MAX_HYST |
60962306a36Sopenharmony_ci			   HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MAX_ALARM |
61062306a36Sopenharmony_ci			   HWMON_T_CRIT_ALARM | HWMON_T_FAULT | HWMON_T_LABEL | HWMON_T_ENABLE,
61162306a36Sopenharmony_ci			   HWMON_T_INPUT | HWMON_T_OFFSET | HWMON_T_MAX | HWMON_T_MAX_HYST |
61262306a36Sopenharmony_ci			   HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MAX_ALARM |
61362306a36Sopenharmony_ci			   HWMON_T_CRIT_ALARM | HWMON_T_FAULT | HWMON_T_LABEL | HWMON_T_ENABLE,
61462306a36Sopenharmony_ci			   HWMON_T_INPUT | HWMON_T_OFFSET | HWMON_T_MAX | HWMON_T_MAX_HYST |
61562306a36Sopenharmony_ci			   HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MAX_ALARM |
61662306a36Sopenharmony_ci			   HWMON_T_CRIT_ALARM | HWMON_T_FAULT | HWMON_T_LABEL | HWMON_T_ENABLE,
61762306a36Sopenharmony_ci			   HWMON_T_INPUT | HWMON_T_OFFSET | HWMON_T_MAX | HWMON_T_MAX_HYST |
61862306a36Sopenharmony_ci			   HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MAX_ALARM |
61962306a36Sopenharmony_ci			   HWMON_T_CRIT_ALARM | HWMON_T_FAULT | HWMON_T_LABEL | HWMON_T_ENABLE,
62062306a36Sopenharmony_ci			   HWMON_T_INPUT | HWMON_T_OFFSET | HWMON_T_MAX | HWMON_T_MAX_HYST |
62162306a36Sopenharmony_ci			   HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MAX_ALARM |
62262306a36Sopenharmony_ci			   HWMON_T_CRIT_ALARM | HWMON_T_FAULT | HWMON_T_LABEL | HWMON_T_ENABLE),
62362306a36Sopenharmony_ci	NULL
62462306a36Sopenharmony_ci};
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_cistatic const struct hwmon_chip_info tmp464_chip_info = {
62762306a36Sopenharmony_ci	.ops = &tmp464_ops,
62862306a36Sopenharmony_ci	.info = tmp464_info,
62962306a36Sopenharmony_ci};
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci/* regmap */
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_cistatic bool tmp464_is_volatile_reg(struct device *dev, unsigned int reg)
63462306a36Sopenharmony_ci{
63562306a36Sopenharmony_ci	return (reg < TMP464_TEMP_REG(TMP468_NUM_CHANNELS) ||
63662306a36Sopenharmony_ci		reg == TMP464_THERM_STATUS_REG ||
63762306a36Sopenharmony_ci		reg == TMP464_THERM2_STATUS_REG ||
63862306a36Sopenharmony_ci		reg == TMP464_REMOTE_OPEN_REG);
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cistatic const struct regmap_config tmp464_regmap_config = {
64262306a36Sopenharmony_ci	.reg_bits = 8,
64362306a36Sopenharmony_ci	.val_bits = 16,
64462306a36Sopenharmony_ci	.max_register = TMP464_DEVICE_ID_REG,
64562306a36Sopenharmony_ci	.volatile_reg = tmp464_is_volatile_reg,
64662306a36Sopenharmony_ci	.val_format_endian = REGMAP_ENDIAN_BIG,
64762306a36Sopenharmony_ci	.cache_type = REGCACHE_MAPLE,
64862306a36Sopenharmony_ci	.use_single_read = true,
64962306a36Sopenharmony_ci	.use_single_write = true,
65062306a36Sopenharmony_ci};
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_cistatic int tmp464_probe(struct i2c_client *client)
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci	struct device *dev = &client->dev;
65562306a36Sopenharmony_ci	struct device *hwmon_dev;
65662306a36Sopenharmony_ci	struct tmp464_data *data;
65762306a36Sopenharmony_ci	int i, err;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
66062306a36Sopenharmony_ci		dev_err(&client->dev, "i2c functionality check failed\n");
66162306a36Sopenharmony_ci		return -ENODEV;
66262306a36Sopenharmony_ci	}
66362306a36Sopenharmony_ci	data = devm_kzalloc(dev, sizeof(struct tmp464_data), GFP_KERNEL);
66462306a36Sopenharmony_ci	if (!data)
66562306a36Sopenharmony_ci		return -ENOMEM;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	mutex_init(&data->update_lock);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	if (dev->of_node)
67062306a36Sopenharmony_ci		data->channels = (int)(unsigned long)of_device_get_match_data(&client->dev);
67162306a36Sopenharmony_ci	else
67262306a36Sopenharmony_ci		data->channels = i2c_match_id(tmp464_id, client)->driver_data;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	data->regmap = devm_regmap_init_i2c(client, &tmp464_regmap_config);
67562306a36Sopenharmony_ci	if (IS_ERR(data->regmap))
67662306a36Sopenharmony_ci		return PTR_ERR(data->regmap);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	for (i = 0; i < data->channels; i++)
67962306a36Sopenharmony_ci		data->channel[i].enabled = true;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	err = tmp464_init_client(dev, data);
68262306a36Sopenharmony_ci	if (err)
68362306a36Sopenharmony_ci		return err;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	if (dev->of_node) {
68662306a36Sopenharmony_ci		err = tmp464_probe_from_dt(dev, data);
68762306a36Sopenharmony_ci		if (err)
68862306a36Sopenharmony_ci			return err;
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
69262306a36Sopenharmony_ci							 data, &tmp464_chip_info, NULL);
69362306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(hwmon_dev);
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_cistatic struct i2c_driver tmp464_driver = {
69762306a36Sopenharmony_ci	.class = I2C_CLASS_HWMON,
69862306a36Sopenharmony_ci	.driver = {
69962306a36Sopenharmony_ci		.name	= "tmp464",
70062306a36Sopenharmony_ci		.of_match_table = of_match_ptr(tmp464_of_match),
70162306a36Sopenharmony_ci	},
70262306a36Sopenharmony_ci	.probe = tmp464_probe,
70362306a36Sopenharmony_ci	.id_table = tmp464_id,
70462306a36Sopenharmony_ci	.detect = tmp464_detect,
70562306a36Sopenharmony_ci	.address_list = normal_i2c,
70662306a36Sopenharmony_ci};
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_cimodule_i2c_driver(tmp464_driver);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ciMODULE_AUTHOR("Agathe Porte <agathe.porte@nokia.com>");
71162306a36Sopenharmony_ciMODULE_DESCRIPTION("Texas Instruments TMP464 temperature sensor driver");
71262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
713