18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Analog Devices LTC2947 high precision power and energy monitor
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2019 Analog Devices Inc.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
88c2ecf20Sopenharmony_ci#include <linux/bits.h>
98c2ecf20Sopenharmony_ci#include <linux/clk.h>
108c2ecf20Sopenharmony_ci#include <linux/device.h>
118c2ecf20Sopenharmony_ci#include <linux/hwmon.h>
128c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/of.h>
158c2ecf20Sopenharmony_ci#include <linux/regmap.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "ltc2947.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/* register's */
208c2ecf20Sopenharmony_ci#define LTC2947_REG_PAGE_CTRL		0xFF
218c2ecf20Sopenharmony_ci#define LTC2947_REG_CTRL		0xF0
228c2ecf20Sopenharmony_ci#define LTC2947_REG_TBCTL		0xE9
238c2ecf20Sopenharmony_ci#define LTC2947_CONT_MODE_MASK		BIT(3)
248c2ecf20Sopenharmony_ci#define LTC2947_CONT_MODE(x)		FIELD_PREP(LTC2947_CONT_MODE_MASK, x)
258c2ecf20Sopenharmony_ci#define LTC2947_PRE_MASK		GENMASK(2, 0)
268c2ecf20Sopenharmony_ci#define LTC2947_PRE(x)			FIELD_PREP(LTC2947_PRE_MASK, x)
278c2ecf20Sopenharmony_ci#define LTC2947_DIV_MASK		GENMASK(7, 3)
288c2ecf20Sopenharmony_ci#define LTC2947_DIV(x)			FIELD_PREP(LTC2947_DIV_MASK, x)
298c2ecf20Sopenharmony_ci#define LTC2947_SHUTDOWN_MASK		BIT(0)
308c2ecf20Sopenharmony_ci#define LTC2947_REG_ACCUM_POL		0xE1
318c2ecf20Sopenharmony_ci#define LTC2947_ACCUM_POL_1_MASK	GENMASK(1, 0)
328c2ecf20Sopenharmony_ci#define LTC2947_ACCUM_POL_1(x)		FIELD_PREP(LTC2947_ACCUM_POL_1_MASK, x)
338c2ecf20Sopenharmony_ci#define LTC2947_ACCUM_POL_2_MASK	GENMASK(3, 2)
348c2ecf20Sopenharmony_ci#define LTC2947_ACCUM_POL_2(x)		FIELD_PREP(LTC2947_ACCUM_POL_2_MASK, x)
358c2ecf20Sopenharmony_ci#define LTC2947_REG_ACCUM_DEADBAND	0xE4
368c2ecf20Sopenharmony_ci#define LTC2947_REG_GPIOSTATCTL		0x67
378c2ecf20Sopenharmony_ci#define LTC2947_GPIO_EN_MASK		BIT(0)
388c2ecf20Sopenharmony_ci#define LTC2947_GPIO_EN(x)		FIELD_PREP(LTC2947_GPIO_EN_MASK, x)
398c2ecf20Sopenharmony_ci#define LTC2947_GPIO_FAN_EN_MASK	BIT(6)
408c2ecf20Sopenharmony_ci#define LTC2947_GPIO_FAN_EN(x)		FIELD_PREP(LTC2947_GPIO_FAN_EN_MASK, x)
418c2ecf20Sopenharmony_ci#define LTC2947_GPIO_FAN_POL_MASK	BIT(7)
428c2ecf20Sopenharmony_ci#define LTC2947_GPIO_FAN_POL(x)		FIELD_PREP(LTC2947_GPIO_FAN_POL_MASK, x)
438c2ecf20Sopenharmony_ci#define LTC2947_REG_GPIO_ACCUM		0xE3
448c2ecf20Sopenharmony_ci/* 200Khz */
458c2ecf20Sopenharmony_ci#define LTC2947_CLK_MIN			200000
468c2ecf20Sopenharmony_ci/* 25Mhz */
478c2ecf20Sopenharmony_ci#define LTC2947_CLK_MAX			25000000
488c2ecf20Sopenharmony_ci#define LTC2947_PAGE0			0
498c2ecf20Sopenharmony_ci#define LTC2947_PAGE1			1
508c2ecf20Sopenharmony_ci/* Voltage registers */
518c2ecf20Sopenharmony_ci#define LTC2947_REG_VOLTAGE		0xA0
528c2ecf20Sopenharmony_ci#define LTC2947_REG_VOLTAGE_MAX		0x50
538c2ecf20Sopenharmony_ci#define LTC2947_REG_VOLTAGE_MIN		0x52
548c2ecf20Sopenharmony_ci#define LTC2947_REG_VOLTAGE_THRE_H	0x90
558c2ecf20Sopenharmony_ci#define LTC2947_REG_VOLTAGE_THRE_L	0x92
568c2ecf20Sopenharmony_ci#define LTC2947_REG_DVCC		0xA4
578c2ecf20Sopenharmony_ci#define LTC2947_REG_DVCC_MAX		0x58
588c2ecf20Sopenharmony_ci#define LTC2947_REG_DVCC_MIN		0x5A
598c2ecf20Sopenharmony_ci#define LTC2947_REG_DVCC_THRE_H		0x98
608c2ecf20Sopenharmony_ci#define LTC2947_REG_DVCC_THRE_L		0x9A
618c2ecf20Sopenharmony_ci#define LTC2947_VOLTAGE_GEN_CHAN	0
628c2ecf20Sopenharmony_ci#define LTC2947_VOLTAGE_DVCC_CHAN	1
638c2ecf20Sopenharmony_ci/* in mV */
648c2ecf20Sopenharmony_ci#define VOLTAGE_MAX			15500
658c2ecf20Sopenharmony_ci#define VOLTAGE_MIN			-300
668c2ecf20Sopenharmony_ci#define VDVCC_MAX			15000
678c2ecf20Sopenharmony_ci#define VDVCC_MIN			4750
688c2ecf20Sopenharmony_ci/* Current registers */
698c2ecf20Sopenharmony_ci#define LTC2947_REG_CURRENT		0x90
708c2ecf20Sopenharmony_ci#define LTC2947_REG_CURRENT_MAX		0x40
718c2ecf20Sopenharmony_ci#define LTC2947_REG_CURRENT_MIN		0x42
728c2ecf20Sopenharmony_ci#define LTC2947_REG_CURRENT_THRE_H	0x80
738c2ecf20Sopenharmony_ci#define LTC2947_REG_CURRENT_THRE_L	0x82
748c2ecf20Sopenharmony_ci/* in mA */
758c2ecf20Sopenharmony_ci#define CURRENT_MAX			30000
768c2ecf20Sopenharmony_ci#define CURRENT_MIN			-30000
778c2ecf20Sopenharmony_ci/* Power registers */
788c2ecf20Sopenharmony_ci#define LTC2947_REG_POWER		0x93
798c2ecf20Sopenharmony_ci#define LTC2947_REG_POWER_MAX		0x44
808c2ecf20Sopenharmony_ci#define LTC2947_REG_POWER_MIN		0x46
818c2ecf20Sopenharmony_ci#define LTC2947_REG_POWER_THRE_H	0x84
828c2ecf20Sopenharmony_ci#define LTC2947_REG_POWER_THRE_L	0x86
838c2ecf20Sopenharmony_ci/* in uW */
848c2ecf20Sopenharmony_ci#define POWER_MAX			450000000
858c2ecf20Sopenharmony_ci#define POWER_MIN			-450000000
868c2ecf20Sopenharmony_ci/* Temperature registers */
878c2ecf20Sopenharmony_ci#define LTC2947_REG_TEMP		0xA2
888c2ecf20Sopenharmony_ci#define LTC2947_REG_TEMP_MAX		0x54
898c2ecf20Sopenharmony_ci#define LTC2947_REG_TEMP_MIN		0x56
908c2ecf20Sopenharmony_ci#define LTC2947_REG_TEMP_THRE_H		0x94
918c2ecf20Sopenharmony_ci#define LTC2947_REG_TEMP_THRE_L		0x96
928c2ecf20Sopenharmony_ci#define LTC2947_REG_TEMP_FAN_THRE_H	0x9C
938c2ecf20Sopenharmony_ci#define LTC2947_REG_TEMP_FAN_THRE_L	0x9E
948c2ecf20Sopenharmony_ci#define LTC2947_TEMP_FAN_CHAN		1
958c2ecf20Sopenharmony_ci/* in millidegress Celsius */
968c2ecf20Sopenharmony_ci#define TEMP_MAX			85000
978c2ecf20Sopenharmony_ci#define TEMP_MIN			-40000
988c2ecf20Sopenharmony_ci/* Energy registers */
998c2ecf20Sopenharmony_ci#define LTC2947_REG_ENERGY1		0x06
1008c2ecf20Sopenharmony_ci#define LTC2947_REG_ENERGY2		0x16
1018c2ecf20Sopenharmony_ci/* Status/Alarm/Overflow registers */
1028c2ecf20Sopenharmony_ci#define LTC2947_REG_STATUS		0x80
1038c2ecf20Sopenharmony_ci#define LTC2947_REG_STATVT		0x81
1048c2ecf20Sopenharmony_ci#define LTC2947_REG_STATIP		0x82
1058c2ecf20Sopenharmony_ci#define LTC2947_REG_STATVDVCC		0x87
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci#define LTC2947_ALERTS_SIZE	(LTC2947_REG_STATVDVCC - LTC2947_REG_STATUS)
1088c2ecf20Sopenharmony_ci#define LTC2947_MAX_VOLTAGE_MASK	BIT(0)
1098c2ecf20Sopenharmony_ci#define LTC2947_MIN_VOLTAGE_MASK	BIT(1)
1108c2ecf20Sopenharmony_ci#define LTC2947_MAX_CURRENT_MASK	BIT(0)
1118c2ecf20Sopenharmony_ci#define LTC2947_MIN_CURRENT_MASK	BIT(1)
1128c2ecf20Sopenharmony_ci#define LTC2947_MAX_POWER_MASK		BIT(2)
1138c2ecf20Sopenharmony_ci#define LTC2947_MIN_POWER_MASK		BIT(3)
1148c2ecf20Sopenharmony_ci#define LTC2947_MAX_TEMP_MASK		BIT(2)
1158c2ecf20Sopenharmony_ci#define LTC2947_MIN_TEMP_MASK		BIT(3)
1168c2ecf20Sopenharmony_ci#define LTC2947_MAX_TEMP_FAN_MASK	BIT(4)
1178c2ecf20Sopenharmony_ci#define LTC2947_MIN_TEMP_FAN_MASK	BIT(5)
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistruct ltc2947_data {
1208c2ecf20Sopenharmony_ci	struct regmap *map;
1218c2ecf20Sopenharmony_ci	struct device *dev;
1228c2ecf20Sopenharmony_ci	/*
1238c2ecf20Sopenharmony_ci	 * The mutex is needed because the device has 2 memory pages. When
1248c2ecf20Sopenharmony_ci	 * reading/writing the correct page needs to be set so that, the
1258c2ecf20Sopenharmony_ci	 * complete sequence select_page->read/write needs to be protected.
1268c2ecf20Sopenharmony_ci	 */
1278c2ecf20Sopenharmony_ci	struct mutex lock;
1288c2ecf20Sopenharmony_ci	u32 lsb_energy;
1298c2ecf20Sopenharmony_ci	bool gpio_out;
1308c2ecf20Sopenharmony_ci};
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic int __ltc2947_val_read16(const struct ltc2947_data *st, const u8 reg,
1338c2ecf20Sopenharmony_ci				u64 *val)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	__be16 __val = 0;
1368c2ecf20Sopenharmony_ci	int ret;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	ret = regmap_bulk_read(st->map, reg, &__val, 2);
1398c2ecf20Sopenharmony_ci	if (ret)
1408c2ecf20Sopenharmony_ci		return ret;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	*val = be16_to_cpu(__val);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	return 0;
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic int __ltc2947_val_read24(const struct ltc2947_data *st, const u8 reg,
1488c2ecf20Sopenharmony_ci				u64 *val)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	__be32 __val = 0;
1518c2ecf20Sopenharmony_ci	int ret;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	ret = regmap_bulk_read(st->map, reg, &__val, 3);
1548c2ecf20Sopenharmony_ci	if (ret)
1558c2ecf20Sopenharmony_ci		return ret;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	*val = be32_to_cpu(__val) >> 8;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	return 0;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic int __ltc2947_val_read64(const struct ltc2947_data *st, const u8 reg,
1638c2ecf20Sopenharmony_ci				u64 *val)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	__be64 __val = 0;
1668c2ecf20Sopenharmony_ci	int ret;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	ret = regmap_bulk_read(st->map, reg, &__val, 6);
1698c2ecf20Sopenharmony_ci	if (ret)
1708c2ecf20Sopenharmony_ci		return ret;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	*val = be64_to_cpu(__val) >> 16;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return 0;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic int ltc2947_val_read(struct ltc2947_data *st, const u8 reg,
1788c2ecf20Sopenharmony_ci			    const u8 page, const size_t size, s64 *val)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	int ret;
1818c2ecf20Sopenharmony_ci	u64 __val = 0;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	mutex_lock(&st->lock);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, page);
1868c2ecf20Sopenharmony_ci	if (ret) {
1878c2ecf20Sopenharmony_ci		mutex_unlock(&st->lock);
1888c2ecf20Sopenharmony_ci		return ret;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	dev_dbg(st->dev, "Read val, reg:%02X, p:%d sz:%zu\n", reg, page,
1928c2ecf20Sopenharmony_ci		size);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	switch (size) {
1958c2ecf20Sopenharmony_ci	case 2:
1968c2ecf20Sopenharmony_ci		ret = __ltc2947_val_read16(st, reg, &__val);
1978c2ecf20Sopenharmony_ci		break;
1988c2ecf20Sopenharmony_ci	case 3:
1998c2ecf20Sopenharmony_ci		ret = __ltc2947_val_read24(st, reg, &__val);
2008c2ecf20Sopenharmony_ci		break;
2018c2ecf20Sopenharmony_ci	case 6:
2028c2ecf20Sopenharmony_ci		ret = __ltc2947_val_read64(st, reg, &__val);
2038c2ecf20Sopenharmony_ci		break;
2048c2ecf20Sopenharmony_ci	default:
2058c2ecf20Sopenharmony_ci		ret = -EINVAL;
2068c2ecf20Sopenharmony_ci		break;
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	mutex_unlock(&st->lock);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (ret)
2128c2ecf20Sopenharmony_ci		return ret;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	*val = sign_extend64(__val, (8 * size) - 1);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	dev_dbg(st->dev, "Got s:%lld, u:%016llX\n", *val, __val);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	return 0;
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic int __ltc2947_val_write64(const struct ltc2947_data *st, const u8 reg,
2228c2ecf20Sopenharmony_ci				 const u64 val)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	__be64 __val;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	__val = cpu_to_be64(val << 16);
2278c2ecf20Sopenharmony_ci	return regmap_bulk_write(st->map, reg, &__val, 6);
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic int __ltc2947_val_write16(const struct ltc2947_data *st, const u8 reg,
2318c2ecf20Sopenharmony_ci				 const u16 val)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	__be16 __val;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	__val = cpu_to_be16(val);
2368c2ecf20Sopenharmony_ci	return regmap_bulk_write(st->map, reg, &__val, 2);
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic int ltc2947_val_write(struct ltc2947_data *st, const u8 reg,
2408c2ecf20Sopenharmony_ci			     const u8 page, const size_t size, const u64 val)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	int ret;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	mutex_lock(&st->lock);
2458c2ecf20Sopenharmony_ci	/* set device on correct page */
2468c2ecf20Sopenharmony_ci	ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, page);
2478c2ecf20Sopenharmony_ci	if (ret) {
2488c2ecf20Sopenharmony_ci		mutex_unlock(&st->lock);
2498c2ecf20Sopenharmony_ci		return ret;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	dev_dbg(st->dev, "Write val, r:%02X, p:%d, sz:%zu, val:%016llX\n",
2538c2ecf20Sopenharmony_ci		reg, page, size, val);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	switch (size) {
2568c2ecf20Sopenharmony_ci	case 2:
2578c2ecf20Sopenharmony_ci		ret = __ltc2947_val_write16(st, reg, val);
2588c2ecf20Sopenharmony_ci		break;
2598c2ecf20Sopenharmony_ci	case 6:
2608c2ecf20Sopenharmony_ci		ret = __ltc2947_val_write64(st, reg, val);
2618c2ecf20Sopenharmony_ci		break;
2628c2ecf20Sopenharmony_ci	default:
2638c2ecf20Sopenharmony_ci		ret = -EINVAL;
2648c2ecf20Sopenharmony_ci		break;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	mutex_unlock(&st->lock);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	return ret;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic int ltc2947_reset_history(struct ltc2947_data *st, const u8 reg_h,
2738c2ecf20Sopenharmony_ci				 const u8 reg_l)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	int ret;
2768c2ecf20Sopenharmony_ci	/*
2778c2ecf20Sopenharmony_ci	 * let's reset the tracking register's. Tracking register's have all
2788c2ecf20Sopenharmony_ci	 * 2 bytes size
2798c2ecf20Sopenharmony_ci	 */
2808c2ecf20Sopenharmony_ci	ret = ltc2947_val_write(st, reg_h, LTC2947_PAGE0, 2, 0x8000U);
2818c2ecf20Sopenharmony_ci	if (ret)
2828c2ecf20Sopenharmony_ci		return ret;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	return ltc2947_val_write(st, reg_l, LTC2947_PAGE0, 2, 0x7FFFU);
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistatic int ltc2947_alarm_read(struct ltc2947_data *st, const u8 reg,
2888c2ecf20Sopenharmony_ci			      const u32 mask, long *val)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	u8 offset = reg - LTC2947_REG_STATUS;
2918c2ecf20Sopenharmony_ci	/* +1 to include status reg */
2928c2ecf20Sopenharmony_ci	char alarms[LTC2947_ALERTS_SIZE + 1];
2938c2ecf20Sopenharmony_ci	int ret = 0;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	memset(alarms, 0, sizeof(alarms));
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	mutex_lock(&st->lock);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, LTC2947_PAGE0);
3008c2ecf20Sopenharmony_ci	if (ret)
3018c2ecf20Sopenharmony_ci		goto unlock;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	dev_dbg(st->dev, "Read alarm, reg:%02X, mask:%02X\n", reg, mask);
3048c2ecf20Sopenharmony_ci	/*
3058c2ecf20Sopenharmony_ci	 * As stated in the datasheet, when Threshold and Overflow registers
3068c2ecf20Sopenharmony_ci	 * are used, the status and all alert registers must be read in one
3078c2ecf20Sopenharmony_ci	 * multi-byte transaction.
3088c2ecf20Sopenharmony_ci	 */
3098c2ecf20Sopenharmony_ci	ret = regmap_bulk_read(st->map, LTC2947_REG_STATUS, alarms,
3108c2ecf20Sopenharmony_ci			       sizeof(alarms));
3118c2ecf20Sopenharmony_ci	if (ret)
3128c2ecf20Sopenharmony_ci		goto unlock;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	/* get the alarm */
3158c2ecf20Sopenharmony_ci	*val = !!(alarms[offset] & mask);
3168c2ecf20Sopenharmony_ciunlock:
3178c2ecf20Sopenharmony_ci	mutex_unlock(&st->lock);
3188c2ecf20Sopenharmony_ci	return ret;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic ssize_t ltc2947_show_value(struct device *dev,
3228c2ecf20Sopenharmony_ci				  struct device_attribute *da, char *buf)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	struct ltc2947_data *st = dev_get_drvdata(dev);
3258c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
3268c2ecf20Sopenharmony_ci	int ret;
3278c2ecf20Sopenharmony_ci	s64 val = 0;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	ret = ltc2947_val_read(st, attr->index, LTC2947_PAGE0, 6, &val);
3308c2ecf20Sopenharmony_ci	if (ret)
3318c2ecf20Sopenharmony_ci		return ret;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	/* value in microJoule. st->lsb_energy was multiplied by 10E9 */
3348c2ecf20Sopenharmony_ci	val = div_s64(val * st->lsb_energy, 1000);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	return sprintf(buf, "%lld\n", val);
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic int ltc2947_read_temp(struct device *dev, const u32 attr, long *val,
3408c2ecf20Sopenharmony_ci			     const int channel)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	int ret;
3438c2ecf20Sopenharmony_ci	struct ltc2947_data *st = dev_get_drvdata(dev);
3448c2ecf20Sopenharmony_ci	s64 __val = 0;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	switch (attr) {
3478c2ecf20Sopenharmony_ci	case hwmon_temp_input:
3488c2ecf20Sopenharmony_ci		ret = ltc2947_val_read(st, LTC2947_REG_TEMP, LTC2947_PAGE0,
3498c2ecf20Sopenharmony_ci				       2, &__val);
3508c2ecf20Sopenharmony_ci		break;
3518c2ecf20Sopenharmony_ci	case hwmon_temp_highest:
3528c2ecf20Sopenharmony_ci		ret = ltc2947_val_read(st, LTC2947_REG_TEMP_MAX, LTC2947_PAGE0,
3538c2ecf20Sopenharmony_ci				       2, &__val);
3548c2ecf20Sopenharmony_ci		break;
3558c2ecf20Sopenharmony_ci	case hwmon_temp_lowest:
3568c2ecf20Sopenharmony_ci		ret = ltc2947_val_read(st, LTC2947_REG_TEMP_MIN, LTC2947_PAGE0,
3578c2ecf20Sopenharmony_ci				       2, &__val);
3588c2ecf20Sopenharmony_ci		break;
3598c2ecf20Sopenharmony_ci	case hwmon_temp_max_alarm:
3608c2ecf20Sopenharmony_ci		if (channel == LTC2947_TEMP_FAN_CHAN)
3618c2ecf20Sopenharmony_ci			return ltc2947_alarm_read(st, LTC2947_REG_STATVT,
3628c2ecf20Sopenharmony_ci						  LTC2947_MAX_TEMP_FAN_MASK,
3638c2ecf20Sopenharmony_ci						  val);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		return ltc2947_alarm_read(st, LTC2947_REG_STATVT,
3668c2ecf20Sopenharmony_ci					  LTC2947_MAX_TEMP_MASK, val);
3678c2ecf20Sopenharmony_ci	case hwmon_temp_min_alarm:
3688c2ecf20Sopenharmony_ci		if (channel == LTC2947_TEMP_FAN_CHAN)
3698c2ecf20Sopenharmony_ci			return	ltc2947_alarm_read(st, LTC2947_REG_STATVT,
3708c2ecf20Sopenharmony_ci						   LTC2947_MIN_TEMP_FAN_MASK,
3718c2ecf20Sopenharmony_ci						   val);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci		return	ltc2947_alarm_read(st, LTC2947_REG_STATVT,
3748c2ecf20Sopenharmony_ci					   LTC2947_MIN_TEMP_MASK, val);
3758c2ecf20Sopenharmony_ci	case hwmon_temp_max:
3768c2ecf20Sopenharmony_ci		if (channel == LTC2947_TEMP_FAN_CHAN)
3778c2ecf20Sopenharmony_ci			ret = ltc2947_val_read(st, LTC2947_REG_TEMP_FAN_THRE_H,
3788c2ecf20Sopenharmony_ci					       LTC2947_PAGE1, 2, &__val);
3798c2ecf20Sopenharmony_ci		else
3808c2ecf20Sopenharmony_ci			ret = ltc2947_val_read(st, LTC2947_REG_TEMP_THRE_H,
3818c2ecf20Sopenharmony_ci					       LTC2947_PAGE1, 2, &__val);
3828c2ecf20Sopenharmony_ci		break;
3838c2ecf20Sopenharmony_ci	case hwmon_temp_min:
3848c2ecf20Sopenharmony_ci		if (channel == LTC2947_TEMP_FAN_CHAN)
3858c2ecf20Sopenharmony_ci			ret = ltc2947_val_read(st, LTC2947_REG_TEMP_FAN_THRE_L,
3868c2ecf20Sopenharmony_ci					       LTC2947_PAGE1, 2, &__val);
3878c2ecf20Sopenharmony_ci		else
3888c2ecf20Sopenharmony_ci			ret = ltc2947_val_read(st, LTC2947_REG_TEMP_THRE_L,
3898c2ecf20Sopenharmony_ci					       LTC2947_PAGE1, 2, &__val);
3908c2ecf20Sopenharmony_ci		break;
3918c2ecf20Sopenharmony_ci	default:
3928c2ecf20Sopenharmony_ci		return -ENOTSUPP;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	if (ret)
3968c2ecf20Sopenharmony_ci		return ret;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	/* in milidegrees celcius, temp is given by: */
3998c2ecf20Sopenharmony_ci	*val = (__val * 204) + 5500;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	return 0;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic int ltc2947_read_power(struct device *dev, const u32 attr, long *val)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	struct ltc2947_data *st = dev_get_drvdata(dev);
4078c2ecf20Sopenharmony_ci	int ret;
4088c2ecf20Sopenharmony_ci	u32 lsb = 200000; /* in uW */
4098c2ecf20Sopenharmony_ci	s64 __val = 0;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	switch (attr) {
4128c2ecf20Sopenharmony_ci	case hwmon_power_input:
4138c2ecf20Sopenharmony_ci		ret = ltc2947_val_read(st, LTC2947_REG_POWER, LTC2947_PAGE0,
4148c2ecf20Sopenharmony_ci				       3, &__val);
4158c2ecf20Sopenharmony_ci		lsb = 50000;
4168c2ecf20Sopenharmony_ci		break;
4178c2ecf20Sopenharmony_ci	case hwmon_power_input_highest:
4188c2ecf20Sopenharmony_ci		ret = ltc2947_val_read(st, LTC2947_REG_POWER_MAX, LTC2947_PAGE0,
4198c2ecf20Sopenharmony_ci				       2, &__val);
4208c2ecf20Sopenharmony_ci		break;
4218c2ecf20Sopenharmony_ci	case hwmon_power_input_lowest:
4228c2ecf20Sopenharmony_ci		ret = ltc2947_val_read(st, LTC2947_REG_POWER_MIN, LTC2947_PAGE0,
4238c2ecf20Sopenharmony_ci				       2, &__val);
4248c2ecf20Sopenharmony_ci		break;
4258c2ecf20Sopenharmony_ci	case hwmon_power_max_alarm:
4268c2ecf20Sopenharmony_ci		return ltc2947_alarm_read(st, LTC2947_REG_STATIP,
4278c2ecf20Sopenharmony_ci					  LTC2947_MAX_POWER_MASK, val);
4288c2ecf20Sopenharmony_ci	case hwmon_power_min_alarm:
4298c2ecf20Sopenharmony_ci		return ltc2947_alarm_read(st, LTC2947_REG_STATIP,
4308c2ecf20Sopenharmony_ci					  LTC2947_MIN_POWER_MASK, val);
4318c2ecf20Sopenharmony_ci	case hwmon_power_max:
4328c2ecf20Sopenharmony_ci		ret = ltc2947_val_read(st, LTC2947_REG_POWER_THRE_H,
4338c2ecf20Sopenharmony_ci				       LTC2947_PAGE1, 2, &__val);
4348c2ecf20Sopenharmony_ci		break;
4358c2ecf20Sopenharmony_ci	case hwmon_power_min:
4368c2ecf20Sopenharmony_ci		ret = ltc2947_val_read(st, LTC2947_REG_POWER_THRE_L,
4378c2ecf20Sopenharmony_ci				       LTC2947_PAGE1, 2, &__val);
4388c2ecf20Sopenharmony_ci		break;
4398c2ecf20Sopenharmony_ci	default:
4408c2ecf20Sopenharmony_ci		return -ENOTSUPP;
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	if (ret)
4448c2ecf20Sopenharmony_ci		return ret;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	*val = __val * lsb;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	return 0;
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic int ltc2947_read_curr(struct device *dev, const u32 attr, long *val)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	struct ltc2947_data *st = dev_get_drvdata(dev);
4548c2ecf20Sopenharmony_ci	int ret;
4558c2ecf20Sopenharmony_ci	u8 lsb = 12; /* in mA */
4568c2ecf20Sopenharmony_ci	s64 __val = 0;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	switch (attr) {
4598c2ecf20Sopenharmony_ci	case hwmon_curr_input:
4608c2ecf20Sopenharmony_ci		ret = ltc2947_val_read(st, LTC2947_REG_CURRENT,
4618c2ecf20Sopenharmony_ci				       LTC2947_PAGE0, 3, &__val);
4628c2ecf20Sopenharmony_ci		lsb = 3;
4638c2ecf20Sopenharmony_ci		break;
4648c2ecf20Sopenharmony_ci	case hwmon_curr_highest:
4658c2ecf20Sopenharmony_ci		ret = ltc2947_val_read(st, LTC2947_REG_CURRENT_MAX,
4668c2ecf20Sopenharmony_ci				       LTC2947_PAGE0, 2, &__val);
4678c2ecf20Sopenharmony_ci		break;
4688c2ecf20Sopenharmony_ci	case hwmon_curr_lowest:
4698c2ecf20Sopenharmony_ci		ret = ltc2947_val_read(st, LTC2947_REG_CURRENT_MIN,
4708c2ecf20Sopenharmony_ci				       LTC2947_PAGE0, 2, &__val);
4718c2ecf20Sopenharmony_ci		break;
4728c2ecf20Sopenharmony_ci	case hwmon_curr_max_alarm:
4738c2ecf20Sopenharmony_ci		return ltc2947_alarm_read(st, LTC2947_REG_STATIP,
4748c2ecf20Sopenharmony_ci					  LTC2947_MAX_CURRENT_MASK, val);
4758c2ecf20Sopenharmony_ci	case hwmon_curr_min_alarm:
4768c2ecf20Sopenharmony_ci		return ltc2947_alarm_read(st, LTC2947_REG_STATIP,
4778c2ecf20Sopenharmony_ci					  LTC2947_MIN_CURRENT_MASK, val);
4788c2ecf20Sopenharmony_ci	case hwmon_curr_max:
4798c2ecf20Sopenharmony_ci		ret = ltc2947_val_read(st, LTC2947_REG_CURRENT_THRE_H,
4808c2ecf20Sopenharmony_ci				       LTC2947_PAGE1, 2, &__val);
4818c2ecf20Sopenharmony_ci		break;
4828c2ecf20Sopenharmony_ci	case hwmon_curr_min:
4838c2ecf20Sopenharmony_ci		ret = ltc2947_val_read(st, LTC2947_REG_CURRENT_THRE_L,
4848c2ecf20Sopenharmony_ci				       LTC2947_PAGE1, 2, &__val);
4858c2ecf20Sopenharmony_ci		break;
4868c2ecf20Sopenharmony_ci	default:
4878c2ecf20Sopenharmony_ci		return -ENOTSUPP;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	if (ret)
4918c2ecf20Sopenharmony_ci		return ret;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	*val = __val * lsb;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	return 0;
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic int ltc2947_read_in(struct device *dev, const u32 attr, long *val,
4998c2ecf20Sopenharmony_ci			   const int channel)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	struct ltc2947_data *st = dev_get_drvdata(dev);
5028c2ecf20Sopenharmony_ci	int ret;
5038c2ecf20Sopenharmony_ci	u8 lsb = 2; /* in mV */
5048c2ecf20Sopenharmony_ci	s64 __val = 0;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	if (channel < 0 || channel > LTC2947_VOLTAGE_DVCC_CHAN) {
5078c2ecf20Sopenharmony_ci		dev_err(st->dev, "Invalid chan%d for voltage", channel);
5088c2ecf20Sopenharmony_ci		return -EINVAL;
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	switch (attr) {
5128c2ecf20Sopenharmony_ci	case hwmon_in_input:
5138c2ecf20Sopenharmony_ci		if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
5148c2ecf20Sopenharmony_ci			ret = ltc2947_val_read(st, LTC2947_REG_DVCC,
5158c2ecf20Sopenharmony_ci					       LTC2947_PAGE0, 2, &__val);
5168c2ecf20Sopenharmony_ci			lsb = 145;
5178c2ecf20Sopenharmony_ci		} else {
5188c2ecf20Sopenharmony_ci			ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE,
5198c2ecf20Sopenharmony_ci					       LTC2947_PAGE0, 2, &__val);
5208c2ecf20Sopenharmony_ci		}
5218c2ecf20Sopenharmony_ci		break;
5228c2ecf20Sopenharmony_ci	case hwmon_in_highest:
5238c2ecf20Sopenharmony_ci		if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
5248c2ecf20Sopenharmony_ci			ret = ltc2947_val_read(st, LTC2947_REG_DVCC_MAX,
5258c2ecf20Sopenharmony_ci					       LTC2947_PAGE0, 2, &__val);
5268c2ecf20Sopenharmony_ci			lsb = 145;
5278c2ecf20Sopenharmony_ci		} else {
5288c2ecf20Sopenharmony_ci			ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE_MAX,
5298c2ecf20Sopenharmony_ci					       LTC2947_PAGE0, 2, &__val);
5308c2ecf20Sopenharmony_ci		}
5318c2ecf20Sopenharmony_ci		break;
5328c2ecf20Sopenharmony_ci	case hwmon_in_lowest:
5338c2ecf20Sopenharmony_ci		if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
5348c2ecf20Sopenharmony_ci			ret = ltc2947_val_read(st, LTC2947_REG_DVCC_MIN,
5358c2ecf20Sopenharmony_ci					       LTC2947_PAGE0, 2, &__val);
5368c2ecf20Sopenharmony_ci			lsb = 145;
5378c2ecf20Sopenharmony_ci		} else {
5388c2ecf20Sopenharmony_ci			ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE_MIN,
5398c2ecf20Sopenharmony_ci					       LTC2947_PAGE0, 2, &__val);
5408c2ecf20Sopenharmony_ci		}
5418c2ecf20Sopenharmony_ci		break;
5428c2ecf20Sopenharmony_ci	case hwmon_in_max_alarm:
5438c2ecf20Sopenharmony_ci		if (channel == LTC2947_VOLTAGE_DVCC_CHAN)
5448c2ecf20Sopenharmony_ci			return ltc2947_alarm_read(st, LTC2947_REG_STATVDVCC,
5458c2ecf20Sopenharmony_ci						  LTC2947_MAX_VOLTAGE_MASK,
5468c2ecf20Sopenharmony_ci						  val);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci		return ltc2947_alarm_read(st, LTC2947_REG_STATVT,
5498c2ecf20Sopenharmony_ci					  LTC2947_MAX_VOLTAGE_MASK, val);
5508c2ecf20Sopenharmony_ci	case hwmon_in_min_alarm:
5518c2ecf20Sopenharmony_ci		if (channel == LTC2947_VOLTAGE_DVCC_CHAN)
5528c2ecf20Sopenharmony_ci			return ltc2947_alarm_read(st, LTC2947_REG_STATVDVCC,
5538c2ecf20Sopenharmony_ci						  LTC2947_MIN_VOLTAGE_MASK,
5548c2ecf20Sopenharmony_ci						  val);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci		return ltc2947_alarm_read(st, LTC2947_REG_STATVT,
5578c2ecf20Sopenharmony_ci					  LTC2947_MIN_VOLTAGE_MASK, val);
5588c2ecf20Sopenharmony_ci	case hwmon_in_max:
5598c2ecf20Sopenharmony_ci		if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
5608c2ecf20Sopenharmony_ci			ret = ltc2947_val_read(st, LTC2947_REG_DVCC_THRE_H,
5618c2ecf20Sopenharmony_ci					       LTC2947_PAGE1, 2, &__val);
5628c2ecf20Sopenharmony_ci			lsb = 145;
5638c2ecf20Sopenharmony_ci		} else {
5648c2ecf20Sopenharmony_ci			ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE_THRE_H,
5658c2ecf20Sopenharmony_ci					       LTC2947_PAGE1, 2, &__val);
5668c2ecf20Sopenharmony_ci		}
5678c2ecf20Sopenharmony_ci		break;
5688c2ecf20Sopenharmony_ci	case hwmon_in_min:
5698c2ecf20Sopenharmony_ci		if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
5708c2ecf20Sopenharmony_ci			ret = ltc2947_val_read(st, LTC2947_REG_DVCC_THRE_L,
5718c2ecf20Sopenharmony_ci					       LTC2947_PAGE1, 2, &__val);
5728c2ecf20Sopenharmony_ci			lsb = 145;
5738c2ecf20Sopenharmony_ci		} else {
5748c2ecf20Sopenharmony_ci			ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE_THRE_L,
5758c2ecf20Sopenharmony_ci					       LTC2947_PAGE1, 2, &__val);
5768c2ecf20Sopenharmony_ci		}
5778c2ecf20Sopenharmony_ci		break;
5788c2ecf20Sopenharmony_ci	default:
5798c2ecf20Sopenharmony_ci		return -ENOTSUPP;
5808c2ecf20Sopenharmony_ci	}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	if (ret)
5838c2ecf20Sopenharmony_ci		return ret;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	*val = __val * lsb;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	return 0;
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_cistatic int ltc2947_read(struct device *dev, enum hwmon_sensor_types type,
5918c2ecf20Sopenharmony_ci			u32 attr, int channel, long *val)
5928c2ecf20Sopenharmony_ci{
5938c2ecf20Sopenharmony_ci	switch (type) {
5948c2ecf20Sopenharmony_ci	case hwmon_in:
5958c2ecf20Sopenharmony_ci		return ltc2947_read_in(dev, attr, val, channel);
5968c2ecf20Sopenharmony_ci	case hwmon_curr:
5978c2ecf20Sopenharmony_ci		return ltc2947_read_curr(dev, attr, val);
5988c2ecf20Sopenharmony_ci	case hwmon_power:
5998c2ecf20Sopenharmony_ci		return ltc2947_read_power(dev, attr, val);
6008c2ecf20Sopenharmony_ci	case hwmon_temp:
6018c2ecf20Sopenharmony_ci		return ltc2947_read_temp(dev, attr, val, channel);
6028c2ecf20Sopenharmony_ci	default:
6038c2ecf20Sopenharmony_ci		return -ENOTSUPP;
6048c2ecf20Sopenharmony_ci	}
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic int ltc2947_write_temp(struct device *dev, const u32 attr,
6088c2ecf20Sopenharmony_ci			      long val, const int channel)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	struct ltc2947_data *st = dev_get_drvdata(dev);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	if (channel < 0 || channel > LTC2947_TEMP_FAN_CHAN) {
6138c2ecf20Sopenharmony_ci		dev_err(st->dev, "Invalid chan%d for temperature", channel);
6148c2ecf20Sopenharmony_ci		return -EINVAL;
6158c2ecf20Sopenharmony_ci	}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	switch (attr) {
6188c2ecf20Sopenharmony_ci	case hwmon_temp_reset_history:
6198c2ecf20Sopenharmony_ci		if (val != 1)
6208c2ecf20Sopenharmony_ci			return -EINVAL;
6218c2ecf20Sopenharmony_ci		return ltc2947_reset_history(st, LTC2947_REG_TEMP_MAX,
6228c2ecf20Sopenharmony_ci					     LTC2947_REG_TEMP_MIN);
6238c2ecf20Sopenharmony_ci	case hwmon_temp_max:
6248c2ecf20Sopenharmony_ci		val = clamp_val(val, TEMP_MIN, TEMP_MAX);
6258c2ecf20Sopenharmony_ci		if (channel == LTC2947_TEMP_FAN_CHAN) {
6268c2ecf20Sopenharmony_ci			if (!st->gpio_out)
6278c2ecf20Sopenharmony_ci				return -ENOTSUPP;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci			return ltc2947_val_write(st,
6308c2ecf20Sopenharmony_ci					LTC2947_REG_TEMP_FAN_THRE_H,
6318c2ecf20Sopenharmony_ci					LTC2947_PAGE1, 2,
6328c2ecf20Sopenharmony_ci					DIV_ROUND_CLOSEST(val - 550, 204));
6338c2ecf20Sopenharmony_ci		}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci		return ltc2947_val_write(st, LTC2947_REG_TEMP_THRE_H,
6368c2ecf20Sopenharmony_ci					 LTC2947_PAGE1, 2,
6378c2ecf20Sopenharmony_ci					 DIV_ROUND_CLOSEST(val - 550, 204));
6388c2ecf20Sopenharmony_ci	case hwmon_temp_min:
6398c2ecf20Sopenharmony_ci		val = clamp_val(val, TEMP_MIN, TEMP_MAX);
6408c2ecf20Sopenharmony_ci		if (channel == LTC2947_TEMP_FAN_CHAN) {
6418c2ecf20Sopenharmony_ci			if (!st->gpio_out)
6428c2ecf20Sopenharmony_ci				return -ENOTSUPP;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci			return ltc2947_val_write(st,
6458c2ecf20Sopenharmony_ci					LTC2947_REG_TEMP_FAN_THRE_L,
6468c2ecf20Sopenharmony_ci					LTC2947_PAGE1, 2,
6478c2ecf20Sopenharmony_ci					DIV_ROUND_CLOSEST(val - 550, 204));
6488c2ecf20Sopenharmony_ci		}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci		return ltc2947_val_write(st, LTC2947_REG_TEMP_THRE_L,
6518c2ecf20Sopenharmony_ci					 LTC2947_PAGE1, 2,
6528c2ecf20Sopenharmony_ci					 DIV_ROUND_CLOSEST(val - 550, 204));
6538c2ecf20Sopenharmony_ci	default:
6548c2ecf20Sopenharmony_ci		return -ENOTSUPP;
6558c2ecf20Sopenharmony_ci	}
6568c2ecf20Sopenharmony_ci}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_cistatic int ltc2947_write_power(struct device *dev, const u32 attr,
6598c2ecf20Sopenharmony_ci			       long val)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	struct ltc2947_data *st = dev_get_drvdata(dev);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	switch (attr) {
6648c2ecf20Sopenharmony_ci	case hwmon_power_reset_history:
6658c2ecf20Sopenharmony_ci		if (val != 1)
6668c2ecf20Sopenharmony_ci			return -EINVAL;
6678c2ecf20Sopenharmony_ci		return ltc2947_reset_history(st, LTC2947_REG_POWER_MAX,
6688c2ecf20Sopenharmony_ci					     LTC2947_REG_POWER_MIN);
6698c2ecf20Sopenharmony_ci	case hwmon_power_max:
6708c2ecf20Sopenharmony_ci		val = clamp_val(val, POWER_MIN, POWER_MAX);
6718c2ecf20Sopenharmony_ci		return ltc2947_val_write(st, LTC2947_REG_POWER_THRE_H,
6728c2ecf20Sopenharmony_ci					 LTC2947_PAGE1, 2,
6738c2ecf20Sopenharmony_ci					 DIV_ROUND_CLOSEST(val, 200000));
6748c2ecf20Sopenharmony_ci	case hwmon_power_min:
6758c2ecf20Sopenharmony_ci		val = clamp_val(val, POWER_MIN, POWER_MAX);
6768c2ecf20Sopenharmony_ci		return ltc2947_val_write(st, LTC2947_REG_POWER_THRE_L,
6778c2ecf20Sopenharmony_ci					 LTC2947_PAGE1, 2,
6788c2ecf20Sopenharmony_ci					 DIV_ROUND_CLOSEST(val, 200000));
6798c2ecf20Sopenharmony_ci	default:
6808c2ecf20Sopenharmony_ci		return -ENOTSUPP;
6818c2ecf20Sopenharmony_ci	}
6828c2ecf20Sopenharmony_ci}
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_cistatic int ltc2947_write_curr(struct device *dev, const u32 attr,
6858c2ecf20Sopenharmony_ci			      long val)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	struct ltc2947_data *st = dev_get_drvdata(dev);
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	switch (attr) {
6908c2ecf20Sopenharmony_ci	case hwmon_curr_reset_history:
6918c2ecf20Sopenharmony_ci		if (val != 1)
6928c2ecf20Sopenharmony_ci			return -EINVAL;
6938c2ecf20Sopenharmony_ci		return ltc2947_reset_history(st, LTC2947_REG_CURRENT_MAX,
6948c2ecf20Sopenharmony_ci					     LTC2947_REG_CURRENT_MIN);
6958c2ecf20Sopenharmony_ci	case hwmon_curr_max:
6968c2ecf20Sopenharmony_ci		val = clamp_val(val, CURRENT_MIN, CURRENT_MAX);
6978c2ecf20Sopenharmony_ci		return ltc2947_val_write(st, LTC2947_REG_CURRENT_THRE_H,
6988c2ecf20Sopenharmony_ci					 LTC2947_PAGE1, 2,
6998c2ecf20Sopenharmony_ci					 DIV_ROUND_CLOSEST(val, 12));
7008c2ecf20Sopenharmony_ci	case hwmon_curr_min:
7018c2ecf20Sopenharmony_ci		val = clamp_val(val, CURRENT_MIN, CURRENT_MAX);
7028c2ecf20Sopenharmony_ci		return ltc2947_val_write(st, LTC2947_REG_CURRENT_THRE_L,
7038c2ecf20Sopenharmony_ci					 LTC2947_PAGE1, 2,
7048c2ecf20Sopenharmony_ci					 DIV_ROUND_CLOSEST(val, 12));
7058c2ecf20Sopenharmony_ci	default:
7068c2ecf20Sopenharmony_ci		return -ENOTSUPP;
7078c2ecf20Sopenharmony_ci	}
7088c2ecf20Sopenharmony_ci}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_cistatic int ltc2947_write_in(struct device *dev, const u32 attr, long val,
7118c2ecf20Sopenharmony_ci			    const int channel)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	struct ltc2947_data *st = dev_get_drvdata(dev);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	if (channel > LTC2947_VOLTAGE_DVCC_CHAN) {
7168c2ecf20Sopenharmony_ci		dev_err(st->dev, "Invalid chan%d for voltage", channel);
7178c2ecf20Sopenharmony_ci		return -EINVAL;
7188c2ecf20Sopenharmony_ci	}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	switch (attr) {
7218c2ecf20Sopenharmony_ci	case hwmon_in_reset_history:
7228c2ecf20Sopenharmony_ci		if (val != 1)
7238c2ecf20Sopenharmony_ci			return -EINVAL;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci		if (channel == LTC2947_VOLTAGE_DVCC_CHAN)
7268c2ecf20Sopenharmony_ci			return ltc2947_reset_history(st, LTC2947_REG_DVCC_MAX,
7278c2ecf20Sopenharmony_ci						     LTC2947_REG_DVCC_MIN);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci		return ltc2947_reset_history(st, LTC2947_REG_VOLTAGE_MAX,
7308c2ecf20Sopenharmony_ci					     LTC2947_REG_VOLTAGE_MIN);
7318c2ecf20Sopenharmony_ci	case hwmon_in_max:
7328c2ecf20Sopenharmony_ci		if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
7338c2ecf20Sopenharmony_ci			val = clamp_val(val, VDVCC_MIN, VDVCC_MAX);
7348c2ecf20Sopenharmony_ci			return ltc2947_val_write(st, LTC2947_REG_DVCC_THRE_H,
7358c2ecf20Sopenharmony_ci						 LTC2947_PAGE1, 2,
7368c2ecf20Sopenharmony_ci						 DIV_ROUND_CLOSEST(val, 145));
7378c2ecf20Sopenharmony_ci		}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci		val = clamp_val(val, VOLTAGE_MIN, VOLTAGE_MAX);
7408c2ecf20Sopenharmony_ci		return ltc2947_val_write(st, LTC2947_REG_VOLTAGE_THRE_H,
7418c2ecf20Sopenharmony_ci					 LTC2947_PAGE1, 2,
7428c2ecf20Sopenharmony_ci					 DIV_ROUND_CLOSEST(val, 2));
7438c2ecf20Sopenharmony_ci	case hwmon_in_min:
7448c2ecf20Sopenharmony_ci		if (channel == LTC2947_VOLTAGE_DVCC_CHAN) {
7458c2ecf20Sopenharmony_ci			val = clamp_val(val, VDVCC_MIN, VDVCC_MAX);
7468c2ecf20Sopenharmony_ci			return ltc2947_val_write(st, LTC2947_REG_DVCC_THRE_L,
7478c2ecf20Sopenharmony_ci						 LTC2947_PAGE1, 2,
7488c2ecf20Sopenharmony_ci						 DIV_ROUND_CLOSEST(val, 145));
7498c2ecf20Sopenharmony_ci		}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci		val = clamp_val(val, VOLTAGE_MIN, VOLTAGE_MAX);
7528c2ecf20Sopenharmony_ci		return ltc2947_val_write(st, LTC2947_REG_VOLTAGE_THRE_L,
7538c2ecf20Sopenharmony_ci					 LTC2947_PAGE1, 2,
7548c2ecf20Sopenharmony_ci					 DIV_ROUND_CLOSEST(val, 2));
7558c2ecf20Sopenharmony_ci	default:
7568c2ecf20Sopenharmony_ci		return -ENOTSUPP;
7578c2ecf20Sopenharmony_ci	}
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_cistatic int ltc2947_write(struct device *dev,
7618c2ecf20Sopenharmony_ci			 enum hwmon_sensor_types type,
7628c2ecf20Sopenharmony_ci			 u32 attr, int channel, long val)
7638c2ecf20Sopenharmony_ci{
7648c2ecf20Sopenharmony_ci	switch (type) {
7658c2ecf20Sopenharmony_ci	case hwmon_in:
7668c2ecf20Sopenharmony_ci		return ltc2947_write_in(dev, attr, val, channel);
7678c2ecf20Sopenharmony_ci	case hwmon_curr:
7688c2ecf20Sopenharmony_ci		return ltc2947_write_curr(dev, attr, val);
7698c2ecf20Sopenharmony_ci	case hwmon_power:
7708c2ecf20Sopenharmony_ci		return ltc2947_write_power(dev, attr, val);
7718c2ecf20Sopenharmony_ci	case hwmon_temp:
7728c2ecf20Sopenharmony_ci		return ltc2947_write_temp(dev, attr, val, channel);
7738c2ecf20Sopenharmony_ci	default:
7748c2ecf20Sopenharmony_ci		return -ENOTSUPP;
7758c2ecf20Sopenharmony_ci	}
7768c2ecf20Sopenharmony_ci}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_cistatic int ltc2947_read_labels(struct device *dev,
7798c2ecf20Sopenharmony_ci			       enum hwmon_sensor_types type,
7808c2ecf20Sopenharmony_ci			       u32 attr, int channel, const char **str)
7818c2ecf20Sopenharmony_ci{
7828c2ecf20Sopenharmony_ci	switch (type) {
7838c2ecf20Sopenharmony_ci	case hwmon_in:
7848c2ecf20Sopenharmony_ci		if (channel == LTC2947_VOLTAGE_DVCC_CHAN)
7858c2ecf20Sopenharmony_ci			*str = "DVCC";
7868c2ecf20Sopenharmony_ci		else
7878c2ecf20Sopenharmony_ci			*str = "VP-VM";
7888c2ecf20Sopenharmony_ci		return 0;
7898c2ecf20Sopenharmony_ci	case hwmon_curr:
7908c2ecf20Sopenharmony_ci		*str = "IP-IM";
7918c2ecf20Sopenharmony_ci		return 0;
7928c2ecf20Sopenharmony_ci	case hwmon_temp:
7938c2ecf20Sopenharmony_ci		if (channel == LTC2947_TEMP_FAN_CHAN)
7948c2ecf20Sopenharmony_ci			*str = "TEMPFAN";
7958c2ecf20Sopenharmony_ci		else
7968c2ecf20Sopenharmony_ci			*str = "Ambient";
7978c2ecf20Sopenharmony_ci		return 0;
7988c2ecf20Sopenharmony_ci	case hwmon_power:
7998c2ecf20Sopenharmony_ci		*str = "Power";
8008c2ecf20Sopenharmony_ci		return 0;
8018c2ecf20Sopenharmony_ci	default:
8028c2ecf20Sopenharmony_ci		return -ENOTSUPP;
8038c2ecf20Sopenharmony_ci	}
8048c2ecf20Sopenharmony_ci}
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_cistatic int ltc2947_in_is_visible(const u32 attr)
8078c2ecf20Sopenharmony_ci{
8088c2ecf20Sopenharmony_ci	switch (attr) {
8098c2ecf20Sopenharmony_ci	case hwmon_in_input:
8108c2ecf20Sopenharmony_ci	case hwmon_in_highest:
8118c2ecf20Sopenharmony_ci	case hwmon_in_lowest:
8128c2ecf20Sopenharmony_ci	case hwmon_in_max_alarm:
8138c2ecf20Sopenharmony_ci	case hwmon_in_min_alarm:
8148c2ecf20Sopenharmony_ci	case hwmon_in_label:
8158c2ecf20Sopenharmony_ci		return 0444;
8168c2ecf20Sopenharmony_ci	case hwmon_in_reset_history:
8178c2ecf20Sopenharmony_ci		return 0200;
8188c2ecf20Sopenharmony_ci	case hwmon_in_max:
8198c2ecf20Sopenharmony_ci	case hwmon_in_min:
8208c2ecf20Sopenharmony_ci		return 0644;
8218c2ecf20Sopenharmony_ci	default:
8228c2ecf20Sopenharmony_ci		return 0;
8238c2ecf20Sopenharmony_ci	}
8248c2ecf20Sopenharmony_ci}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_cistatic int ltc2947_curr_is_visible(const u32 attr)
8278c2ecf20Sopenharmony_ci{
8288c2ecf20Sopenharmony_ci	switch (attr) {
8298c2ecf20Sopenharmony_ci	case hwmon_curr_input:
8308c2ecf20Sopenharmony_ci	case hwmon_curr_highest:
8318c2ecf20Sopenharmony_ci	case hwmon_curr_lowest:
8328c2ecf20Sopenharmony_ci	case hwmon_curr_max_alarm:
8338c2ecf20Sopenharmony_ci	case hwmon_curr_min_alarm:
8348c2ecf20Sopenharmony_ci	case hwmon_curr_label:
8358c2ecf20Sopenharmony_ci		return 0444;
8368c2ecf20Sopenharmony_ci	case hwmon_curr_reset_history:
8378c2ecf20Sopenharmony_ci		return 0200;
8388c2ecf20Sopenharmony_ci	case hwmon_curr_max:
8398c2ecf20Sopenharmony_ci	case hwmon_curr_min:
8408c2ecf20Sopenharmony_ci		return 0644;
8418c2ecf20Sopenharmony_ci	default:
8428c2ecf20Sopenharmony_ci		return 0;
8438c2ecf20Sopenharmony_ci	}
8448c2ecf20Sopenharmony_ci}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_cistatic int ltc2947_power_is_visible(const u32 attr)
8478c2ecf20Sopenharmony_ci{
8488c2ecf20Sopenharmony_ci	switch (attr) {
8498c2ecf20Sopenharmony_ci	case hwmon_power_input:
8508c2ecf20Sopenharmony_ci	case hwmon_power_input_highest:
8518c2ecf20Sopenharmony_ci	case hwmon_power_input_lowest:
8528c2ecf20Sopenharmony_ci	case hwmon_power_label:
8538c2ecf20Sopenharmony_ci	case hwmon_power_max_alarm:
8548c2ecf20Sopenharmony_ci	case hwmon_power_min_alarm:
8558c2ecf20Sopenharmony_ci		return 0444;
8568c2ecf20Sopenharmony_ci	case hwmon_power_reset_history:
8578c2ecf20Sopenharmony_ci		return 0200;
8588c2ecf20Sopenharmony_ci	case hwmon_power_max:
8598c2ecf20Sopenharmony_ci	case hwmon_power_min:
8608c2ecf20Sopenharmony_ci		return 0644;
8618c2ecf20Sopenharmony_ci	default:
8628c2ecf20Sopenharmony_ci		return 0;
8638c2ecf20Sopenharmony_ci	}
8648c2ecf20Sopenharmony_ci}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_cistatic int ltc2947_temp_is_visible(const u32 attr)
8678c2ecf20Sopenharmony_ci{
8688c2ecf20Sopenharmony_ci	switch (attr) {
8698c2ecf20Sopenharmony_ci	case hwmon_temp_input:
8708c2ecf20Sopenharmony_ci	case hwmon_temp_highest:
8718c2ecf20Sopenharmony_ci	case hwmon_temp_lowest:
8728c2ecf20Sopenharmony_ci	case hwmon_temp_max_alarm:
8738c2ecf20Sopenharmony_ci	case hwmon_temp_min_alarm:
8748c2ecf20Sopenharmony_ci	case hwmon_temp_label:
8758c2ecf20Sopenharmony_ci		return 0444;
8768c2ecf20Sopenharmony_ci	case hwmon_temp_reset_history:
8778c2ecf20Sopenharmony_ci		return 0200;
8788c2ecf20Sopenharmony_ci	case hwmon_temp_max:
8798c2ecf20Sopenharmony_ci	case hwmon_temp_min:
8808c2ecf20Sopenharmony_ci		return 0644;
8818c2ecf20Sopenharmony_ci	default:
8828c2ecf20Sopenharmony_ci		return 0;
8838c2ecf20Sopenharmony_ci	}
8848c2ecf20Sopenharmony_ci}
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_cistatic umode_t ltc2947_is_visible(const void *data,
8878c2ecf20Sopenharmony_ci				  enum hwmon_sensor_types type,
8888c2ecf20Sopenharmony_ci				  u32 attr, int channel)
8898c2ecf20Sopenharmony_ci{
8908c2ecf20Sopenharmony_ci	switch (type) {
8918c2ecf20Sopenharmony_ci	case hwmon_in:
8928c2ecf20Sopenharmony_ci		return ltc2947_in_is_visible(attr);
8938c2ecf20Sopenharmony_ci	case hwmon_curr:
8948c2ecf20Sopenharmony_ci		return ltc2947_curr_is_visible(attr);
8958c2ecf20Sopenharmony_ci	case hwmon_power:
8968c2ecf20Sopenharmony_ci		return ltc2947_power_is_visible(attr);
8978c2ecf20Sopenharmony_ci	case hwmon_temp:
8988c2ecf20Sopenharmony_ci		return ltc2947_temp_is_visible(attr);
8998c2ecf20Sopenharmony_ci	default:
9008c2ecf20Sopenharmony_ci		return 0;
9018c2ecf20Sopenharmony_ci	}
9028c2ecf20Sopenharmony_ci}
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_cistatic const struct hwmon_channel_info *ltc2947_info[] = {
9058c2ecf20Sopenharmony_ci	HWMON_CHANNEL_INFO(in,
9068c2ecf20Sopenharmony_ci			   HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |
9078c2ecf20Sopenharmony_ci			   HWMON_I_MAX | HWMON_I_MIN | HWMON_I_RESET_HISTORY |
9088c2ecf20Sopenharmony_ci			   HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM |
9098c2ecf20Sopenharmony_ci			   HWMON_I_LABEL,
9108c2ecf20Sopenharmony_ci			   HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST |
9118c2ecf20Sopenharmony_ci			   HWMON_I_MAX | HWMON_I_MIN | HWMON_I_RESET_HISTORY |
9128c2ecf20Sopenharmony_ci			   HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM |
9138c2ecf20Sopenharmony_ci			   HWMON_I_LABEL),
9148c2ecf20Sopenharmony_ci	HWMON_CHANNEL_INFO(curr,
9158c2ecf20Sopenharmony_ci			   HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST |
9168c2ecf20Sopenharmony_ci			   HWMON_C_MAX | HWMON_C_MIN | HWMON_C_RESET_HISTORY |
9178c2ecf20Sopenharmony_ci			   HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM |
9188c2ecf20Sopenharmony_ci			   HWMON_C_LABEL),
9198c2ecf20Sopenharmony_ci	HWMON_CHANNEL_INFO(power,
9208c2ecf20Sopenharmony_ci			   HWMON_P_INPUT | HWMON_P_INPUT_LOWEST |
9218c2ecf20Sopenharmony_ci			   HWMON_P_INPUT_HIGHEST | HWMON_P_MAX | HWMON_P_MIN |
9228c2ecf20Sopenharmony_ci			   HWMON_P_RESET_HISTORY | HWMON_P_MAX_ALARM |
9238c2ecf20Sopenharmony_ci			   HWMON_P_MIN_ALARM | HWMON_P_LABEL),
9248c2ecf20Sopenharmony_ci	HWMON_CHANNEL_INFO(temp,
9258c2ecf20Sopenharmony_ci			   HWMON_T_INPUT | HWMON_T_LOWEST | HWMON_T_HIGHEST |
9268c2ecf20Sopenharmony_ci			   HWMON_T_MAX | HWMON_T_MIN | HWMON_T_RESET_HISTORY |
9278c2ecf20Sopenharmony_ci			   HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
9288c2ecf20Sopenharmony_ci			   HWMON_T_LABEL,
9298c2ecf20Sopenharmony_ci			   HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM | HWMON_T_MAX |
9308c2ecf20Sopenharmony_ci			   HWMON_T_MIN | HWMON_T_LABEL),
9318c2ecf20Sopenharmony_ci	NULL
9328c2ecf20Sopenharmony_ci};
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_cistatic const struct hwmon_ops ltc2947_hwmon_ops = {
9358c2ecf20Sopenharmony_ci	.is_visible = ltc2947_is_visible,
9368c2ecf20Sopenharmony_ci	.read = ltc2947_read,
9378c2ecf20Sopenharmony_ci	.write = ltc2947_write,
9388c2ecf20Sopenharmony_ci	.read_string = ltc2947_read_labels,
9398c2ecf20Sopenharmony_ci};
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_cistatic const struct hwmon_chip_info ltc2947_chip_info = {
9428c2ecf20Sopenharmony_ci	.ops = &ltc2947_hwmon_ops,
9438c2ecf20Sopenharmony_ci	.info = ltc2947_info,
9448c2ecf20Sopenharmony_ci};
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci/* energy attributes are 6bytes wide so we need u64 */
9478c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(energy1_input, 0444, ltc2947_show_value, NULL,
9488c2ecf20Sopenharmony_ci			  LTC2947_REG_ENERGY1);
9498c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(energy2_input, 0444, ltc2947_show_value, NULL,
9508c2ecf20Sopenharmony_ci			  LTC2947_REG_ENERGY2);
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_cistatic struct attribute *ltc2947_attrs[] = {
9538c2ecf20Sopenharmony_ci	&sensor_dev_attr_energy1_input.dev_attr.attr,
9548c2ecf20Sopenharmony_ci	&sensor_dev_attr_energy2_input.dev_attr.attr,
9558c2ecf20Sopenharmony_ci	NULL,
9568c2ecf20Sopenharmony_ci};
9578c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(ltc2947);
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_cistatic void ltc2947_clk_disable(void *data)
9608c2ecf20Sopenharmony_ci{
9618c2ecf20Sopenharmony_ci	struct clk *extclk = data;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	clk_disable_unprepare(extclk);
9648c2ecf20Sopenharmony_ci}
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_cistatic int ltc2947_setup(struct ltc2947_data *st)
9678c2ecf20Sopenharmony_ci{
9688c2ecf20Sopenharmony_ci	int ret;
9698c2ecf20Sopenharmony_ci	struct clk *extclk;
9708c2ecf20Sopenharmony_ci	u32 dummy, deadband, pol;
9718c2ecf20Sopenharmony_ci	u32 accum[2];
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	/* clear status register by reading it */
9748c2ecf20Sopenharmony_ci	ret = regmap_read(st->map, LTC2947_REG_STATUS, &dummy);
9758c2ecf20Sopenharmony_ci	if (ret)
9768c2ecf20Sopenharmony_ci		return ret;
9778c2ecf20Sopenharmony_ci	/*
9788c2ecf20Sopenharmony_ci	 * Set max/min for power here since the default values x scale
9798c2ecf20Sopenharmony_ci	 * would overflow on 32bit arch
9808c2ecf20Sopenharmony_ci	 */
9818c2ecf20Sopenharmony_ci	ret = ltc2947_val_write(st, LTC2947_REG_POWER_THRE_H, LTC2947_PAGE1, 2,
9828c2ecf20Sopenharmony_ci				POWER_MAX / 200000);
9838c2ecf20Sopenharmony_ci	if (ret)
9848c2ecf20Sopenharmony_ci		return ret;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	ret = ltc2947_val_write(st, LTC2947_REG_POWER_THRE_L, LTC2947_PAGE1, 2,
9878c2ecf20Sopenharmony_ci				POWER_MIN / 200000);
9888c2ecf20Sopenharmony_ci	if (ret)
9898c2ecf20Sopenharmony_ci		return ret;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	/* check external clock presence */
9928c2ecf20Sopenharmony_ci	extclk = devm_clk_get_optional(st->dev, NULL);
9938c2ecf20Sopenharmony_ci	if (IS_ERR(extclk))
9948c2ecf20Sopenharmony_ci		return dev_err_probe(st->dev, PTR_ERR(extclk),
9958c2ecf20Sopenharmony_ci				     "Failed to get external clock\n");
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	if (extclk) {
9988c2ecf20Sopenharmony_ci		unsigned long rate_hz;
9998c2ecf20Sopenharmony_ci		u8 pre = 0, div, tbctl;
10008c2ecf20Sopenharmony_ci		u64 aux;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci		/* let's calculate and set the right valus in TBCTL */
10038c2ecf20Sopenharmony_ci		rate_hz = clk_get_rate(extclk);
10048c2ecf20Sopenharmony_ci		if (rate_hz < LTC2947_CLK_MIN || rate_hz > LTC2947_CLK_MAX) {
10058c2ecf20Sopenharmony_ci			dev_err(st->dev, "Invalid rate:%lu for external clock",
10068c2ecf20Sopenharmony_ci				rate_hz);
10078c2ecf20Sopenharmony_ci			return -EINVAL;
10088c2ecf20Sopenharmony_ci		}
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(extclk);
10118c2ecf20Sopenharmony_ci		if (ret)
10128c2ecf20Sopenharmony_ci			return ret;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci		ret = devm_add_action_or_reset(st->dev, ltc2947_clk_disable,
10158c2ecf20Sopenharmony_ci					       extclk);
10168c2ecf20Sopenharmony_ci		if (ret)
10178c2ecf20Sopenharmony_ci			return ret;
10188c2ecf20Sopenharmony_ci		/* as in table 1 of the datasheet */
10198c2ecf20Sopenharmony_ci		if (rate_hz >= LTC2947_CLK_MIN && rate_hz <= 1000000)
10208c2ecf20Sopenharmony_ci			pre = 0;
10218c2ecf20Sopenharmony_ci		else if (rate_hz > 1000000 && rate_hz <= 2000000)
10228c2ecf20Sopenharmony_ci			pre = 1;
10238c2ecf20Sopenharmony_ci		else if (rate_hz > 2000000 && rate_hz <= 4000000)
10248c2ecf20Sopenharmony_ci			pre = 2;
10258c2ecf20Sopenharmony_ci		else if (rate_hz > 4000000 && rate_hz <= 8000000)
10268c2ecf20Sopenharmony_ci			pre = 3;
10278c2ecf20Sopenharmony_ci		else if (rate_hz > 8000000 && rate_hz <= 16000000)
10288c2ecf20Sopenharmony_ci			pre = 4;
10298c2ecf20Sopenharmony_ci		else if (rate_hz > 16000000 && rate_hz <= LTC2947_CLK_MAX)
10308c2ecf20Sopenharmony_ci			pre = 5;
10318c2ecf20Sopenharmony_ci		/*
10328c2ecf20Sopenharmony_ci		 * Div is given by:
10338c2ecf20Sopenharmony_ci		 *	floor(fref / (2^PRE * 32768))
10348c2ecf20Sopenharmony_ci		 */
10358c2ecf20Sopenharmony_ci		div = rate_hz / ((1 << pre) * 32768);
10368c2ecf20Sopenharmony_ci		tbctl = LTC2947_PRE(pre) | LTC2947_DIV(div);
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci		ret = regmap_write(st->map, LTC2947_REG_TBCTL, tbctl);
10398c2ecf20Sopenharmony_ci		if (ret)
10408c2ecf20Sopenharmony_ci			return ret;
10418c2ecf20Sopenharmony_ci		/*
10428c2ecf20Sopenharmony_ci		 * The energy lsb is given by (in W*s):
10438c2ecf20Sopenharmony_ci		 *      06416 * (1/fref) * 2^PRE * (DIV + 1)
10448c2ecf20Sopenharmony_ci		 * The value is multiplied by 10E9
10458c2ecf20Sopenharmony_ci		 */
10468c2ecf20Sopenharmony_ci		aux = (div + 1) * ((1 << pre) * 641600000ULL);
10478c2ecf20Sopenharmony_ci		st->lsb_energy = DIV_ROUND_CLOSEST_ULL(aux, rate_hz);
10488c2ecf20Sopenharmony_ci	} else {
10498c2ecf20Sopenharmony_ci		/* 19.89E-6 * 10E9 */
10508c2ecf20Sopenharmony_ci		st->lsb_energy = 19890;
10518c2ecf20Sopenharmony_ci	}
10528c2ecf20Sopenharmony_ci	ret = of_property_read_u32_array(st->dev->of_node,
10538c2ecf20Sopenharmony_ci					 "adi,accumulator-ctl-pol", accum,
10548c2ecf20Sopenharmony_ci					  ARRAY_SIZE(accum));
10558c2ecf20Sopenharmony_ci	if (!ret) {
10568c2ecf20Sopenharmony_ci		u32 accum_reg = LTC2947_ACCUM_POL_1(accum[0]) |
10578c2ecf20Sopenharmony_ci				LTC2947_ACCUM_POL_2(accum[1]);
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci		ret = regmap_write(st->map, LTC2947_REG_ACCUM_POL, accum_reg);
10608c2ecf20Sopenharmony_ci		if (ret)
10618c2ecf20Sopenharmony_ci			return ret;
10628c2ecf20Sopenharmony_ci	}
10638c2ecf20Sopenharmony_ci	ret = of_property_read_u32(st->dev->of_node,
10648c2ecf20Sopenharmony_ci				   "adi,accumulation-deadband-microamp",
10658c2ecf20Sopenharmony_ci				   &deadband);
10668c2ecf20Sopenharmony_ci	if (!ret) {
10678c2ecf20Sopenharmony_ci		/* the LSB is the same as the current, so 3mA */
10688c2ecf20Sopenharmony_ci		ret = regmap_write(st->map, LTC2947_REG_ACCUM_DEADBAND,
10698c2ecf20Sopenharmony_ci				   deadband / (1000 * 3));
10708c2ecf20Sopenharmony_ci		if (ret)
10718c2ecf20Sopenharmony_ci			return ret;
10728c2ecf20Sopenharmony_ci	}
10738c2ecf20Sopenharmony_ci	/* check gpio cfg */
10748c2ecf20Sopenharmony_ci	ret = of_property_read_u32(st->dev->of_node, "adi,gpio-out-pol", &pol);
10758c2ecf20Sopenharmony_ci	if (!ret) {
10768c2ecf20Sopenharmony_ci		/* setup GPIO as output */
10778c2ecf20Sopenharmony_ci		u32 gpio_ctl = LTC2947_GPIO_EN(1) | LTC2947_GPIO_FAN_EN(1) |
10788c2ecf20Sopenharmony_ci			LTC2947_GPIO_FAN_POL(pol);
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci		st->gpio_out = true;
10818c2ecf20Sopenharmony_ci		ret = regmap_write(st->map, LTC2947_REG_GPIOSTATCTL, gpio_ctl);
10828c2ecf20Sopenharmony_ci		if (ret)
10838c2ecf20Sopenharmony_ci			return ret;
10848c2ecf20Sopenharmony_ci	}
10858c2ecf20Sopenharmony_ci	ret = of_property_read_u32_array(st->dev->of_node, "adi,gpio-in-accum",
10868c2ecf20Sopenharmony_ci					 accum, ARRAY_SIZE(accum));
10878c2ecf20Sopenharmony_ci	if (!ret) {
10888c2ecf20Sopenharmony_ci		/*
10898c2ecf20Sopenharmony_ci		 * Setup the accum options. The gpioctl is already defined as
10908c2ecf20Sopenharmony_ci		 * input by default.
10918c2ecf20Sopenharmony_ci		 */
10928c2ecf20Sopenharmony_ci		u32 accum_val = LTC2947_ACCUM_POL_1(accum[0]) |
10938c2ecf20Sopenharmony_ci				LTC2947_ACCUM_POL_2(accum[1]);
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci		if (st->gpio_out) {
10968c2ecf20Sopenharmony_ci			dev_err(st->dev,
10978c2ecf20Sopenharmony_ci				"Cannot have input gpio config if already configured as output");
10988c2ecf20Sopenharmony_ci			return -EINVAL;
10998c2ecf20Sopenharmony_ci		}
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci		ret = regmap_write(st->map, LTC2947_REG_GPIO_ACCUM, accum_val);
11028c2ecf20Sopenharmony_ci		if (ret)
11038c2ecf20Sopenharmony_ci			return ret;
11048c2ecf20Sopenharmony_ci	}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	/* set continuos mode */
11078c2ecf20Sopenharmony_ci	return regmap_update_bits(st->map, LTC2947_REG_CTRL,
11088c2ecf20Sopenharmony_ci				  LTC2947_CONT_MODE_MASK, LTC2947_CONT_MODE(1));
11098c2ecf20Sopenharmony_ci}
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ciint ltc2947_core_probe(struct regmap *map, const char *name)
11128c2ecf20Sopenharmony_ci{
11138c2ecf20Sopenharmony_ci	struct ltc2947_data *st;
11148c2ecf20Sopenharmony_ci	struct device *dev = regmap_get_device(map);
11158c2ecf20Sopenharmony_ci	struct device *hwmon;
11168c2ecf20Sopenharmony_ci	int ret;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
11198c2ecf20Sopenharmony_ci	if (!st)
11208c2ecf20Sopenharmony_ci		return -ENOMEM;
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	st->map = map;
11238c2ecf20Sopenharmony_ci	st->dev = dev;
11248c2ecf20Sopenharmony_ci	dev_set_drvdata(dev, st);
11258c2ecf20Sopenharmony_ci	mutex_init(&st->lock);
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	ret = ltc2947_setup(st);
11288c2ecf20Sopenharmony_ci	if (ret)
11298c2ecf20Sopenharmony_ci		return ret;
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	hwmon = devm_hwmon_device_register_with_info(dev, name, st,
11328c2ecf20Sopenharmony_ci						     &ltc2947_chip_info,
11338c2ecf20Sopenharmony_ci						     ltc2947_groups);
11348c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(hwmon);
11358c2ecf20Sopenharmony_ci}
11368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ltc2947_core_probe);
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_cistatic int __maybe_unused ltc2947_resume(struct device *dev)
11398c2ecf20Sopenharmony_ci{
11408c2ecf20Sopenharmony_ci	struct ltc2947_data *st = dev_get_drvdata(dev);
11418c2ecf20Sopenharmony_ci	u32 ctrl = 0;
11428c2ecf20Sopenharmony_ci	int ret;
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	/* dummy read to wake the device */
11458c2ecf20Sopenharmony_ci	ret = regmap_read(st->map, LTC2947_REG_CTRL, &ctrl);
11468c2ecf20Sopenharmony_ci	if (ret)
11478c2ecf20Sopenharmony_ci		return ret;
11488c2ecf20Sopenharmony_ci	/*
11498c2ecf20Sopenharmony_ci	 * Wait for the device. It takes 100ms to wake up so, 10ms extra
11508c2ecf20Sopenharmony_ci	 * should be enough.
11518c2ecf20Sopenharmony_ci	 */
11528c2ecf20Sopenharmony_ci	msleep(110);
11538c2ecf20Sopenharmony_ci	ret = regmap_read(st->map, LTC2947_REG_CTRL, &ctrl);
11548c2ecf20Sopenharmony_ci	if (ret)
11558c2ecf20Sopenharmony_ci		return ret;
11568c2ecf20Sopenharmony_ci	/* ctrl should be 0 */
11578c2ecf20Sopenharmony_ci	if (ctrl != 0) {
11588c2ecf20Sopenharmony_ci		dev_err(st->dev, "Device failed to wake up, ctl:%02X\n", ctrl);
11598c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
11608c2ecf20Sopenharmony_ci	}
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	/* set continuous mode */
11638c2ecf20Sopenharmony_ci	return regmap_update_bits(st->map, LTC2947_REG_CTRL,
11648c2ecf20Sopenharmony_ci				  LTC2947_CONT_MODE_MASK, LTC2947_CONT_MODE(1));
11658c2ecf20Sopenharmony_ci}
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_cistatic int __maybe_unused ltc2947_suspend(struct device *dev)
11688c2ecf20Sopenharmony_ci{
11698c2ecf20Sopenharmony_ci	struct ltc2947_data *st = dev_get_drvdata(dev);
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	return regmap_update_bits(st->map, LTC2947_REG_CTRL,
11728c2ecf20Sopenharmony_ci				  LTC2947_SHUTDOWN_MASK, 1);
11738c2ecf20Sopenharmony_ci}
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ciSIMPLE_DEV_PM_OPS(ltc2947_pm_ops, ltc2947_suspend, ltc2947_resume);
11768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ltc2947_pm_ops);
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ciconst struct of_device_id ltc2947_of_match[] = {
11798c2ecf20Sopenharmony_ci	{ .compatible = "adi,ltc2947" },
11808c2ecf20Sopenharmony_ci	{}
11818c2ecf20Sopenharmony_ci};
11828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ltc2947_of_match);
11838c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ltc2947_of_match);
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
11868c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("LTC2947 power and energy monitor core driver");
11878c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1188