|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Analog Devices LTC2947 high precision power and energy monitor | 
|  | * | 
|  | * Copyright 2019 Analog Devices Inc. | 
|  | */ | 
|  | #include <linux/bitfield.h> | 
|  | #include <linux/bits.h> | 
|  | #include <linux/clk.h> | 
|  | #include <linux/device.h> | 
|  | #include <linux/hwmon.h> | 
|  | #include <linux/hwmon-sysfs.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/of.h> | 
|  | #include <linux/regmap.h> | 
|  |  | 
|  | #include "ltc2947.h" | 
|  |  | 
|  | /* register's */ | 
|  | #define LTC2947_REG_PAGE_CTRL		0xFF | 
|  | #define LTC2947_REG_CTRL		0xF0 | 
|  | #define LTC2947_REG_TBCTL		0xE9 | 
|  | #define LTC2947_CONT_MODE_MASK		BIT(3) | 
|  | #define LTC2947_CONT_MODE(x)		FIELD_PREP(LTC2947_CONT_MODE_MASK, x) | 
|  | #define LTC2947_PRE_MASK		GENMASK(2, 0) | 
|  | #define LTC2947_PRE(x)			FIELD_PREP(LTC2947_PRE_MASK, x) | 
|  | #define LTC2947_DIV_MASK		GENMASK(7, 3) | 
|  | #define LTC2947_DIV(x)			FIELD_PREP(LTC2947_DIV_MASK, x) | 
|  | #define LTC2947_SHUTDOWN_MASK		BIT(0) | 
|  | #define LTC2947_REG_ACCUM_POL		0xE1 | 
|  | #define LTC2947_ACCUM_POL_1_MASK	GENMASK(1, 0) | 
|  | #define LTC2947_ACCUM_POL_1(x)		FIELD_PREP(LTC2947_ACCUM_POL_1_MASK, x) | 
|  | #define LTC2947_ACCUM_POL_2_MASK	GENMASK(3, 2) | 
|  | #define LTC2947_ACCUM_POL_2(x)		FIELD_PREP(LTC2947_ACCUM_POL_2_MASK, x) | 
|  | #define LTC2947_REG_ACCUM_DEADBAND	0xE4 | 
|  | #define LTC2947_REG_GPIOSTATCTL		0x67 | 
|  | #define LTC2947_GPIO_EN_MASK		BIT(0) | 
|  | #define LTC2947_GPIO_EN(x)		FIELD_PREP(LTC2947_GPIO_EN_MASK, x) | 
|  | #define LTC2947_GPIO_FAN_EN_MASK	BIT(6) | 
|  | #define LTC2947_GPIO_FAN_EN(x)		FIELD_PREP(LTC2947_GPIO_FAN_EN_MASK, x) | 
|  | #define LTC2947_GPIO_FAN_POL_MASK	BIT(7) | 
|  | #define LTC2947_GPIO_FAN_POL(x)		FIELD_PREP(LTC2947_GPIO_FAN_POL_MASK, x) | 
|  | #define LTC2947_REG_GPIO_ACCUM		0xE3 | 
|  | /* 200Khz */ | 
|  | #define LTC2947_CLK_MIN			200000 | 
|  | /* 25Mhz */ | 
|  | #define LTC2947_CLK_MAX			25000000 | 
|  | #define LTC2947_PAGE0			0 | 
|  | #define LTC2947_PAGE1			1 | 
|  | /* Voltage registers */ | 
|  | #define LTC2947_REG_VOLTAGE		0xA0 | 
|  | #define LTC2947_REG_VOLTAGE_MAX		0x50 | 
|  | #define LTC2947_REG_VOLTAGE_MIN		0x52 | 
|  | #define LTC2947_REG_VOLTAGE_THRE_H	0x90 | 
|  | #define LTC2947_REG_VOLTAGE_THRE_L	0x92 | 
|  | #define LTC2947_REG_DVCC		0xA4 | 
|  | #define LTC2947_REG_DVCC_MAX		0x58 | 
|  | #define LTC2947_REG_DVCC_MIN		0x5A | 
|  | #define LTC2947_REG_DVCC_THRE_H		0x98 | 
|  | #define LTC2947_REG_DVCC_THRE_L		0x9A | 
|  | #define LTC2947_VOLTAGE_GEN_CHAN	0 | 
|  | #define LTC2947_VOLTAGE_DVCC_CHAN	1 | 
|  | /* in mV */ | 
|  | #define VOLTAGE_MAX			15500 | 
|  | #define VOLTAGE_MIN			-300 | 
|  | #define VDVCC_MAX			15000 | 
|  | #define VDVCC_MIN			4750 | 
|  | /* Current registers */ | 
|  | #define LTC2947_REG_CURRENT		0x90 | 
|  | #define LTC2947_REG_CURRENT_MAX		0x40 | 
|  | #define LTC2947_REG_CURRENT_MIN		0x42 | 
|  | #define LTC2947_REG_CURRENT_THRE_H	0x80 | 
|  | #define LTC2947_REG_CURRENT_THRE_L	0x82 | 
|  | /* in mA */ | 
|  | #define CURRENT_MAX			30000 | 
|  | #define CURRENT_MIN			-30000 | 
|  | /* Power registers */ | 
|  | #define LTC2947_REG_POWER		0x93 | 
|  | #define LTC2947_REG_POWER_MAX		0x44 | 
|  | #define LTC2947_REG_POWER_MIN		0x46 | 
|  | #define LTC2947_REG_POWER_THRE_H	0x84 | 
|  | #define LTC2947_REG_POWER_THRE_L	0x86 | 
|  | /* in uW */ | 
|  | #define POWER_MAX			450000000 | 
|  | #define POWER_MIN			-450000000 | 
|  | /* Temperature registers */ | 
|  | #define LTC2947_REG_TEMP		0xA2 | 
|  | #define LTC2947_REG_TEMP_MAX		0x54 | 
|  | #define LTC2947_REG_TEMP_MIN		0x56 | 
|  | #define LTC2947_REG_TEMP_THRE_H		0x94 | 
|  | #define LTC2947_REG_TEMP_THRE_L		0x96 | 
|  | #define LTC2947_REG_TEMP_FAN_THRE_H	0x9C | 
|  | #define LTC2947_REG_TEMP_FAN_THRE_L	0x9E | 
|  | #define LTC2947_TEMP_FAN_CHAN		1 | 
|  | /* in millidegress Celsius */ | 
|  | #define TEMP_MAX			85000 | 
|  | #define TEMP_MIN			-40000 | 
|  | /* Energy registers */ | 
|  | #define LTC2947_REG_ENERGY1		0x06 | 
|  | #define LTC2947_REG_ENERGY2		0x16 | 
|  | /* Status/Alarm/Overflow registers */ | 
|  | #define LTC2947_REG_STATUS		0x80 | 
|  | #define LTC2947_REG_STATVT		0x81 | 
|  | #define LTC2947_REG_STATIP		0x82 | 
|  | #define LTC2947_REG_STATVDVCC		0x87 | 
|  |  | 
|  | #define LTC2947_ALERTS_SIZE	(LTC2947_REG_STATVDVCC - LTC2947_REG_STATUS) | 
|  | #define LTC2947_MAX_VOLTAGE_MASK	BIT(0) | 
|  | #define LTC2947_MIN_VOLTAGE_MASK	BIT(1) | 
|  | #define LTC2947_MAX_CURRENT_MASK	BIT(0) | 
|  | #define LTC2947_MIN_CURRENT_MASK	BIT(1) | 
|  | #define LTC2947_MAX_POWER_MASK		BIT(2) | 
|  | #define LTC2947_MIN_POWER_MASK		BIT(3) | 
|  | #define LTC2947_MAX_TEMP_MASK		BIT(2) | 
|  | #define LTC2947_MIN_TEMP_MASK		BIT(3) | 
|  | #define LTC2947_MAX_TEMP_FAN_MASK	BIT(4) | 
|  | #define LTC2947_MIN_TEMP_FAN_MASK	BIT(5) | 
|  |  | 
|  | struct ltc2947_data { | 
|  | struct regmap *map; | 
|  | struct device *dev; | 
|  | /* | 
|  | * The mutex is needed because the device has 2 memory pages. When | 
|  | * reading/writing the correct page needs to be set so that, the | 
|  | * complete sequence select_page->read/write needs to be protected. | 
|  | */ | 
|  | struct mutex lock; | 
|  | u32 lsb_energy; | 
|  | bool gpio_out; | 
|  | }; | 
|  |  | 
|  | static int __ltc2947_val_read16(const struct ltc2947_data *st, const u8 reg, | 
|  | u64 *val) | 
|  | { | 
|  | __be16 __val = 0; | 
|  | int ret; | 
|  |  | 
|  | ret = regmap_bulk_read(st->map, reg, &__val, 2); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | *val = be16_to_cpu(__val); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int __ltc2947_val_read24(const struct ltc2947_data *st, const u8 reg, | 
|  | u64 *val) | 
|  | { | 
|  | __be32 __val = 0; | 
|  | int ret; | 
|  |  | 
|  | ret = regmap_bulk_read(st->map, reg, &__val, 3); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | *val = be32_to_cpu(__val) >> 8; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int __ltc2947_val_read64(const struct ltc2947_data *st, const u8 reg, | 
|  | u64 *val) | 
|  | { | 
|  | __be64 __val = 0; | 
|  | int ret; | 
|  |  | 
|  | ret = regmap_bulk_read(st->map, reg, &__val, 6); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | *val = be64_to_cpu(__val) >> 16; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int ltc2947_val_read(struct ltc2947_data *st, const u8 reg, | 
|  | const u8 page, const size_t size, s64 *val) | 
|  | { | 
|  | int ret; | 
|  | u64 __val = 0; | 
|  |  | 
|  | mutex_lock(&st->lock); | 
|  |  | 
|  | ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, page); | 
|  | if (ret) { | 
|  | mutex_unlock(&st->lock); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | dev_dbg(st->dev, "Read val, reg:%02X, p:%d sz:%zu\n", reg, page, | 
|  | size); | 
|  |  | 
|  | switch (size) { | 
|  | case 2: | 
|  | ret = __ltc2947_val_read16(st, reg, &__val); | 
|  | break; | 
|  | case 3: | 
|  | ret = __ltc2947_val_read24(st, reg, &__val); | 
|  | break; | 
|  | case 6: | 
|  | ret = __ltc2947_val_read64(st, reg, &__val); | 
|  | break; | 
|  | default: | 
|  | ret = -EINVAL; | 
|  | break; | 
|  | } | 
|  |  | 
|  | mutex_unlock(&st->lock); | 
|  |  | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | *val = sign_extend64(__val, (8 * size) - 1); | 
|  |  | 
|  | dev_dbg(st->dev, "Got s:%lld, u:%016llX\n", *val, __val); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int __ltc2947_val_write64(const struct ltc2947_data *st, const u8 reg, | 
|  | const u64 val) | 
|  | { | 
|  | __be64 __val; | 
|  |  | 
|  | __val = cpu_to_be64(val << 16); | 
|  | return regmap_bulk_write(st->map, reg, &__val, 6); | 
|  | } | 
|  |  | 
|  | static int __ltc2947_val_write16(const struct ltc2947_data *st, const u8 reg, | 
|  | const u16 val) | 
|  | { | 
|  | __be16 __val; | 
|  |  | 
|  | __val = cpu_to_be16(val); | 
|  | return regmap_bulk_write(st->map, reg, &__val, 2); | 
|  | } | 
|  |  | 
|  | static int ltc2947_val_write(struct ltc2947_data *st, const u8 reg, | 
|  | const u8 page, const size_t size, const u64 val) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | mutex_lock(&st->lock); | 
|  | /* set device on correct page */ | 
|  | ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, page); | 
|  | if (ret) { | 
|  | mutex_unlock(&st->lock); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | dev_dbg(st->dev, "Write val, r:%02X, p:%d, sz:%zu, val:%016llX\n", | 
|  | reg, page, size, val); | 
|  |  | 
|  | switch (size) { | 
|  | case 2: | 
|  | ret = __ltc2947_val_write16(st, reg, val); | 
|  | break; | 
|  | case 6: | 
|  | ret = __ltc2947_val_write64(st, reg, val); | 
|  | break; | 
|  | default: | 
|  | ret = -EINVAL; | 
|  | break; | 
|  | } | 
|  |  | 
|  | mutex_unlock(&st->lock); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int ltc2947_reset_history(struct ltc2947_data *st, const u8 reg_h, | 
|  | const u8 reg_l) | 
|  | { | 
|  | int ret; | 
|  | /* | 
|  | * let's reset the tracking register's. Tracking register's have all | 
|  | * 2 bytes size | 
|  | */ | 
|  | ret = ltc2947_val_write(st, reg_h, LTC2947_PAGE0, 2, 0x8000U); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | return ltc2947_val_write(st, reg_l, LTC2947_PAGE0, 2, 0x7FFFU); | 
|  | } | 
|  |  | 
|  | static int ltc2947_alarm_read(struct ltc2947_data *st, const u8 reg, | 
|  | const u32 mask, long *val) | 
|  | { | 
|  | u8 offset = reg - LTC2947_REG_STATUS; | 
|  | /* +1 to include status reg */ | 
|  | char alarms[LTC2947_ALERTS_SIZE + 1]; | 
|  | int ret = 0; | 
|  |  | 
|  | memset(alarms, 0, sizeof(alarms)); | 
|  |  | 
|  | mutex_lock(&st->lock); | 
|  |  | 
|  | ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, LTC2947_PAGE0); | 
|  | if (ret) | 
|  | goto unlock; | 
|  |  | 
|  | dev_dbg(st->dev, "Read alarm, reg:%02X, mask:%02X\n", reg, mask); | 
|  | /* | 
|  | * As stated in the datasheet, when Threshold and Overflow registers | 
|  | * are used, the status and all alert registers must be read in one | 
|  | * multi-byte transaction. | 
|  | */ | 
|  | ret = regmap_bulk_read(st->map, LTC2947_REG_STATUS, alarms, | 
|  | sizeof(alarms)); | 
|  | if (ret) | 
|  | goto unlock; | 
|  |  | 
|  | /* get the alarm */ | 
|  | *val = !!(alarms[offset] & mask); | 
|  | unlock: | 
|  | mutex_unlock(&st->lock); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static ssize_t ltc2947_show_value(struct device *dev, | 
|  | struct device_attribute *da, char *buf) | 
|  | { | 
|  | struct ltc2947_data *st = dev_get_drvdata(dev); | 
|  | struct sensor_device_attribute *attr = to_sensor_dev_attr(da); | 
|  | int ret; | 
|  | s64 val = 0; | 
|  |  | 
|  | ret = ltc2947_val_read(st, attr->index, LTC2947_PAGE0, 6, &val); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | /* value in microJoule. st->lsb_energy was multiplied by 10E9 */ | 
|  | val = div_s64(val * st->lsb_energy, 1000); | 
|  |  | 
|  | return sprintf(buf, "%lld\n", val); | 
|  | } | 
|  |  | 
|  | static int ltc2947_read_temp(struct device *dev, const u32 attr, long *val, | 
|  | const int channel) | 
|  | { | 
|  | int ret; | 
|  | struct ltc2947_data *st = dev_get_drvdata(dev); | 
|  | s64 __val = 0; | 
|  |  | 
|  | switch (attr) { | 
|  | case hwmon_temp_input: | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_TEMP, LTC2947_PAGE0, | 
|  | 2, &__val); | 
|  | break; | 
|  | case hwmon_temp_highest: | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_TEMP_MAX, LTC2947_PAGE0, | 
|  | 2, &__val); | 
|  | break; | 
|  | case hwmon_temp_lowest: | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_TEMP_MIN, LTC2947_PAGE0, | 
|  | 2, &__val); | 
|  | break; | 
|  | case hwmon_temp_max_alarm: | 
|  | if (channel == LTC2947_TEMP_FAN_CHAN) | 
|  | return ltc2947_alarm_read(st, LTC2947_REG_STATVT, | 
|  | LTC2947_MAX_TEMP_FAN_MASK, | 
|  | val); | 
|  |  | 
|  | return ltc2947_alarm_read(st, LTC2947_REG_STATVT, | 
|  | LTC2947_MAX_TEMP_MASK, val); | 
|  | case hwmon_temp_min_alarm: | 
|  | if (channel == LTC2947_TEMP_FAN_CHAN) | 
|  | return	ltc2947_alarm_read(st, LTC2947_REG_STATVT, | 
|  | LTC2947_MIN_TEMP_FAN_MASK, | 
|  | val); | 
|  |  | 
|  | return	ltc2947_alarm_read(st, LTC2947_REG_STATVT, | 
|  | LTC2947_MIN_TEMP_MASK, val); | 
|  | case hwmon_temp_max: | 
|  | if (channel == LTC2947_TEMP_FAN_CHAN) | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_TEMP_FAN_THRE_H, | 
|  | LTC2947_PAGE1, 2, &__val); | 
|  | else | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_TEMP_THRE_H, | 
|  | LTC2947_PAGE1, 2, &__val); | 
|  | break; | 
|  | case hwmon_temp_min: | 
|  | if (channel == LTC2947_TEMP_FAN_CHAN) | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_TEMP_FAN_THRE_L, | 
|  | LTC2947_PAGE1, 2, &__val); | 
|  | else | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_TEMP_THRE_L, | 
|  | LTC2947_PAGE1, 2, &__val); | 
|  | break; | 
|  | default: | 
|  | return -ENOTSUPP; | 
|  | } | 
|  |  | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | /* in milidegrees celcius, temp is given by: */ | 
|  | *val = (__val * 204) + 550; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int ltc2947_read_power(struct device *dev, const u32 attr, long *val) | 
|  | { | 
|  | struct ltc2947_data *st = dev_get_drvdata(dev); | 
|  | int ret; | 
|  | u32 lsb = 200000; /* in uW */ | 
|  | s64 __val = 0; | 
|  |  | 
|  | switch (attr) { | 
|  | case hwmon_power_input: | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_POWER, LTC2947_PAGE0, | 
|  | 3, &__val); | 
|  | lsb = 50000; | 
|  | break; | 
|  | case hwmon_power_input_highest: | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_POWER_MAX, LTC2947_PAGE0, | 
|  | 2, &__val); | 
|  | break; | 
|  | case hwmon_power_input_lowest: | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_POWER_MIN, LTC2947_PAGE0, | 
|  | 2, &__val); | 
|  | break; | 
|  | case hwmon_power_max_alarm: | 
|  | return ltc2947_alarm_read(st, LTC2947_REG_STATIP, | 
|  | LTC2947_MAX_POWER_MASK, val); | 
|  | case hwmon_power_min_alarm: | 
|  | return ltc2947_alarm_read(st, LTC2947_REG_STATIP, | 
|  | LTC2947_MIN_POWER_MASK, val); | 
|  | case hwmon_power_max: | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_POWER_THRE_H, | 
|  | LTC2947_PAGE1, 2, &__val); | 
|  | break; | 
|  | case hwmon_power_min: | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_POWER_THRE_L, | 
|  | LTC2947_PAGE1, 2, &__val); | 
|  | break; | 
|  | default: | 
|  | return -ENOTSUPP; | 
|  | } | 
|  |  | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | *val = __val * lsb; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int ltc2947_read_curr(struct device *dev, const u32 attr, long *val) | 
|  | { | 
|  | struct ltc2947_data *st = dev_get_drvdata(dev); | 
|  | int ret; | 
|  | u8 lsb = 12; /* in mA */ | 
|  | s64 __val = 0; | 
|  |  | 
|  | switch (attr) { | 
|  | case hwmon_curr_input: | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_CURRENT, | 
|  | LTC2947_PAGE0, 3, &__val); | 
|  | lsb = 3; | 
|  | break; | 
|  | case hwmon_curr_highest: | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_CURRENT_MAX, | 
|  | LTC2947_PAGE0, 2, &__val); | 
|  | break; | 
|  | case hwmon_curr_lowest: | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_CURRENT_MIN, | 
|  | LTC2947_PAGE0, 2, &__val); | 
|  | break; | 
|  | case hwmon_curr_max_alarm: | 
|  | return ltc2947_alarm_read(st, LTC2947_REG_STATIP, | 
|  | LTC2947_MAX_CURRENT_MASK, val); | 
|  | case hwmon_curr_min_alarm: | 
|  | return ltc2947_alarm_read(st, LTC2947_REG_STATIP, | 
|  | LTC2947_MIN_CURRENT_MASK, val); | 
|  | case hwmon_curr_max: | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_CURRENT_THRE_H, | 
|  | LTC2947_PAGE1, 2, &__val); | 
|  | break; | 
|  | case hwmon_curr_min: | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_CURRENT_THRE_L, | 
|  | LTC2947_PAGE1, 2, &__val); | 
|  | break; | 
|  | default: | 
|  | return -ENOTSUPP; | 
|  | } | 
|  |  | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | *val = __val * lsb; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int ltc2947_read_in(struct device *dev, const u32 attr, long *val, | 
|  | const int channel) | 
|  | { | 
|  | struct ltc2947_data *st = dev_get_drvdata(dev); | 
|  | int ret; | 
|  | u8 lsb = 2; /* in mV */ | 
|  | s64 __val = 0; | 
|  |  | 
|  | if (channel < 0 || channel > LTC2947_VOLTAGE_DVCC_CHAN) { | 
|  | dev_err(st->dev, "Invalid chan%d for voltage", channel); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | switch (attr) { | 
|  | case hwmon_in_input: | 
|  | if (channel == LTC2947_VOLTAGE_DVCC_CHAN) { | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_DVCC, | 
|  | LTC2947_PAGE0, 2, &__val); | 
|  | lsb = 145; | 
|  | } else { | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE, | 
|  | LTC2947_PAGE0, 2, &__val); | 
|  | } | 
|  | break; | 
|  | case hwmon_in_highest: | 
|  | if (channel == LTC2947_VOLTAGE_DVCC_CHAN) { | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_DVCC_MAX, | 
|  | LTC2947_PAGE0, 2, &__val); | 
|  | lsb = 145; | 
|  | } else { | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE_MAX, | 
|  | LTC2947_PAGE0, 2, &__val); | 
|  | } | 
|  | break; | 
|  | case hwmon_in_lowest: | 
|  | if (channel == LTC2947_VOLTAGE_DVCC_CHAN) { | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_DVCC_MIN, | 
|  | LTC2947_PAGE0, 2, &__val); | 
|  | lsb = 145; | 
|  | } else { | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE_MIN, | 
|  | LTC2947_PAGE0, 2, &__val); | 
|  | } | 
|  | break; | 
|  | case hwmon_in_max_alarm: | 
|  | if (channel == LTC2947_VOLTAGE_DVCC_CHAN) | 
|  | return ltc2947_alarm_read(st, LTC2947_REG_STATVDVCC, | 
|  | LTC2947_MAX_VOLTAGE_MASK, | 
|  | val); | 
|  |  | 
|  | return ltc2947_alarm_read(st, LTC2947_REG_STATVT, | 
|  | LTC2947_MAX_VOLTAGE_MASK, val); | 
|  | case hwmon_in_min_alarm: | 
|  | if (channel == LTC2947_VOLTAGE_DVCC_CHAN) | 
|  | return ltc2947_alarm_read(st, LTC2947_REG_STATVDVCC, | 
|  | LTC2947_MIN_VOLTAGE_MASK, | 
|  | val); | 
|  |  | 
|  | return ltc2947_alarm_read(st, LTC2947_REG_STATVT, | 
|  | LTC2947_MIN_VOLTAGE_MASK, val); | 
|  | case hwmon_in_max: | 
|  | if (channel == LTC2947_VOLTAGE_DVCC_CHAN) { | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_DVCC_THRE_H, | 
|  | LTC2947_PAGE1, 2, &__val); | 
|  | lsb = 145; | 
|  | } else { | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE_THRE_H, | 
|  | LTC2947_PAGE1, 2, &__val); | 
|  | } | 
|  | break; | 
|  | case hwmon_in_min: | 
|  | if (channel == LTC2947_VOLTAGE_DVCC_CHAN) { | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_DVCC_THRE_L, | 
|  | LTC2947_PAGE1, 2, &__val); | 
|  | lsb = 145; | 
|  | } else { | 
|  | ret = ltc2947_val_read(st, LTC2947_REG_VOLTAGE_THRE_L, | 
|  | LTC2947_PAGE1, 2, &__val); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | return -ENOTSUPP; | 
|  | } | 
|  |  | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | *val = __val * lsb; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int ltc2947_read(struct device *dev, enum hwmon_sensor_types type, | 
|  | u32 attr, int channel, long *val) | 
|  | { | 
|  | switch (type) { | 
|  | case hwmon_in: | 
|  | return ltc2947_read_in(dev, attr, val, channel); | 
|  | case hwmon_curr: | 
|  | return ltc2947_read_curr(dev, attr, val); | 
|  | case hwmon_power: | 
|  | return ltc2947_read_power(dev, attr, val); | 
|  | case hwmon_temp: | 
|  | return ltc2947_read_temp(dev, attr, val, channel); | 
|  | default: | 
|  | return -ENOTSUPP; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int ltc2947_write_temp(struct device *dev, const u32 attr, | 
|  | long val, const int channel) | 
|  | { | 
|  | struct ltc2947_data *st = dev_get_drvdata(dev); | 
|  |  | 
|  | if (channel < 0 || channel > LTC2947_TEMP_FAN_CHAN) { | 
|  | dev_err(st->dev, "Invalid chan%d for temperature", channel); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | switch (attr) { | 
|  | case hwmon_temp_reset_history: | 
|  | if (val != 1) | 
|  | return -EINVAL; | 
|  | return ltc2947_reset_history(st, LTC2947_REG_TEMP_MAX, | 
|  | LTC2947_REG_TEMP_MIN); | 
|  | case hwmon_temp_max: | 
|  | val = clamp_val(val, TEMP_MIN, TEMP_MAX); | 
|  | if (channel == LTC2947_TEMP_FAN_CHAN) { | 
|  | if (!st->gpio_out) | 
|  | return -ENOTSUPP; | 
|  |  | 
|  | return ltc2947_val_write(st, | 
|  | LTC2947_REG_TEMP_FAN_THRE_H, | 
|  | LTC2947_PAGE1, 2, | 
|  | DIV_ROUND_CLOSEST(val - 550, 204)); | 
|  | } | 
|  |  | 
|  | return ltc2947_val_write(st, LTC2947_REG_TEMP_THRE_H, | 
|  | LTC2947_PAGE1, 2, | 
|  | DIV_ROUND_CLOSEST(val - 550, 204)); | 
|  | case hwmon_temp_min: | 
|  | val = clamp_val(val, TEMP_MIN, TEMP_MAX); | 
|  | if (channel == LTC2947_TEMP_FAN_CHAN) { | 
|  | if (!st->gpio_out) | 
|  | return -ENOTSUPP; | 
|  |  | 
|  | return ltc2947_val_write(st, | 
|  | LTC2947_REG_TEMP_FAN_THRE_L, | 
|  | LTC2947_PAGE1, 2, | 
|  | DIV_ROUND_CLOSEST(val - 550, 204)); | 
|  | } | 
|  |  | 
|  | return ltc2947_val_write(st, LTC2947_REG_TEMP_THRE_L, | 
|  | LTC2947_PAGE1, 2, | 
|  | DIV_ROUND_CLOSEST(val - 550, 204)); | 
|  | default: | 
|  | return -ENOTSUPP; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int ltc2947_write_power(struct device *dev, const u32 attr, | 
|  | long val) | 
|  | { | 
|  | struct ltc2947_data *st = dev_get_drvdata(dev); | 
|  |  | 
|  | switch (attr) { | 
|  | case hwmon_power_reset_history: | 
|  | if (val != 1) | 
|  | return -EINVAL; | 
|  | return ltc2947_reset_history(st, LTC2947_REG_POWER_MAX, | 
|  | LTC2947_REG_POWER_MIN); | 
|  | case hwmon_power_max: | 
|  | val = clamp_val(val, POWER_MIN, POWER_MAX); | 
|  | return ltc2947_val_write(st, LTC2947_REG_POWER_THRE_H, | 
|  | LTC2947_PAGE1, 2, | 
|  | DIV_ROUND_CLOSEST(val, 200000)); | 
|  | case hwmon_power_min: | 
|  | val = clamp_val(val, POWER_MIN, POWER_MAX); | 
|  | return ltc2947_val_write(st, LTC2947_REG_POWER_THRE_L, | 
|  | LTC2947_PAGE1, 2, | 
|  | DIV_ROUND_CLOSEST(val, 200000)); | 
|  | default: | 
|  | return -ENOTSUPP; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int ltc2947_write_curr(struct device *dev, const u32 attr, | 
|  | long val) | 
|  | { | 
|  | struct ltc2947_data *st = dev_get_drvdata(dev); | 
|  |  | 
|  | switch (attr) { | 
|  | case hwmon_curr_reset_history: | 
|  | if (val != 1) | 
|  | return -EINVAL; | 
|  | return ltc2947_reset_history(st, LTC2947_REG_CURRENT_MAX, | 
|  | LTC2947_REG_CURRENT_MIN); | 
|  | case hwmon_curr_max: | 
|  | val = clamp_val(val, CURRENT_MIN, CURRENT_MAX); | 
|  | return ltc2947_val_write(st, LTC2947_REG_CURRENT_THRE_H, | 
|  | LTC2947_PAGE1, 2, | 
|  | DIV_ROUND_CLOSEST(val, 12)); | 
|  | case hwmon_curr_min: | 
|  | val = clamp_val(val, CURRENT_MIN, CURRENT_MAX); | 
|  | return ltc2947_val_write(st, LTC2947_REG_CURRENT_THRE_L, | 
|  | LTC2947_PAGE1, 2, | 
|  | DIV_ROUND_CLOSEST(val, 12)); | 
|  | default: | 
|  | return -ENOTSUPP; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int ltc2947_write_in(struct device *dev, const u32 attr, long val, | 
|  | const int channel) | 
|  | { | 
|  | struct ltc2947_data *st = dev_get_drvdata(dev); | 
|  |  | 
|  | if (channel > LTC2947_VOLTAGE_DVCC_CHAN) { | 
|  | dev_err(st->dev, "Invalid chan%d for voltage", channel); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | switch (attr) { | 
|  | case hwmon_in_reset_history: | 
|  | if (val != 1) | 
|  | return -EINVAL; | 
|  |  | 
|  | if (channel == LTC2947_VOLTAGE_DVCC_CHAN) | 
|  | return ltc2947_reset_history(st, LTC2947_REG_DVCC_MAX, | 
|  | LTC2947_REG_DVCC_MIN); | 
|  |  | 
|  | return ltc2947_reset_history(st, LTC2947_REG_VOLTAGE_MAX, | 
|  | LTC2947_REG_VOLTAGE_MIN); | 
|  | case hwmon_in_max: | 
|  | if (channel == LTC2947_VOLTAGE_DVCC_CHAN) { | 
|  | val = clamp_val(val, VDVCC_MIN, VDVCC_MAX); | 
|  | return ltc2947_val_write(st, LTC2947_REG_DVCC_THRE_H, | 
|  | LTC2947_PAGE1, 2, | 
|  | DIV_ROUND_CLOSEST(val, 145)); | 
|  | } | 
|  |  | 
|  | val = clamp_val(val, VOLTAGE_MIN, VOLTAGE_MAX); | 
|  | return ltc2947_val_write(st, LTC2947_REG_VOLTAGE_THRE_H, | 
|  | LTC2947_PAGE1, 2, | 
|  | DIV_ROUND_CLOSEST(val, 2)); | 
|  | case hwmon_in_min: | 
|  | if (channel == LTC2947_VOLTAGE_DVCC_CHAN) { | 
|  | val = clamp_val(val, VDVCC_MIN, VDVCC_MAX); | 
|  | return ltc2947_val_write(st, LTC2947_REG_DVCC_THRE_L, | 
|  | LTC2947_PAGE1, 2, | 
|  | DIV_ROUND_CLOSEST(val, 145)); | 
|  | } | 
|  |  | 
|  | val = clamp_val(val, VOLTAGE_MIN, VOLTAGE_MAX); | 
|  | return ltc2947_val_write(st, LTC2947_REG_VOLTAGE_THRE_L, | 
|  | LTC2947_PAGE1, 2, | 
|  | DIV_ROUND_CLOSEST(val, 2)); | 
|  | default: | 
|  | return -ENOTSUPP; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int ltc2947_write(struct device *dev, | 
|  | enum hwmon_sensor_types type, | 
|  | u32 attr, int channel, long val) | 
|  | { | 
|  | switch (type) { | 
|  | case hwmon_in: | 
|  | return ltc2947_write_in(dev, attr, val, channel); | 
|  | case hwmon_curr: | 
|  | return ltc2947_write_curr(dev, attr, val); | 
|  | case hwmon_power: | 
|  | return ltc2947_write_power(dev, attr, val); | 
|  | case hwmon_temp: | 
|  | return ltc2947_write_temp(dev, attr, val, channel); | 
|  | default: | 
|  | return -ENOTSUPP; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int ltc2947_read_labels(struct device *dev, | 
|  | enum hwmon_sensor_types type, | 
|  | u32 attr, int channel, const char **str) | 
|  | { | 
|  | switch (type) { | 
|  | case hwmon_in: | 
|  | if (channel == LTC2947_VOLTAGE_DVCC_CHAN) | 
|  | *str = "DVCC"; | 
|  | else | 
|  | *str = "VP-VM"; | 
|  | return 0; | 
|  | case hwmon_curr: | 
|  | *str = "IP-IM"; | 
|  | return 0; | 
|  | case hwmon_temp: | 
|  | if (channel == LTC2947_TEMP_FAN_CHAN) | 
|  | *str = "TEMPFAN"; | 
|  | else | 
|  | *str = "Ambient"; | 
|  | return 0; | 
|  | case hwmon_power: | 
|  | *str = "Power"; | 
|  | return 0; | 
|  | default: | 
|  | return -ENOTSUPP; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int ltc2947_in_is_visible(const u32 attr) | 
|  | { | 
|  | switch (attr) { | 
|  | case hwmon_in_input: | 
|  | case hwmon_in_highest: | 
|  | case hwmon_in_lowest: | 
|  | case hwmon_in_max_alarm: | 
|  | case hwmon_in_min_alarm: | 
|  | case hwmon_in_label: | 
|  | return 0444; | 
|  | case hwmon_in_reset_history: | 
|  | return 0200; | 
|  | case hwmon_in_max: | 
|  | case hwmon_in_min: | 
|  | return 0644; | 
|  | default: | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int ltc2947_curr_is_visible(const u32 attr) | 
|  | { | 
|  | switch (attr) { | 
|  | case hwmon_curr_input: | 
|  | case hwmon_curr_highest: | 
|  | case hwmon_curr_lowest: | 
|  | case hwmon_curr_max_alarm: | 
|  | case hwmon_curr_min_alarm: | 
|  | case hwmon_curr_label: | 
|  | return 0444; | 
|  | case hwmon_curr_reset_history: | 
|  | return 0200; | 
|  | case hwmon_curr_max: | 
|  | case hwmon_curr_min: | 
|  | return 0644; | 
|  | default: | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int ltc2947_power_is_visible(const u32 attr) | 
|  | { | 
|  | switch (attr) { | 
|  | case hwmon_power_input: | 
|  | case hwmon_power_input_highest: | 
|  | case hwmon_power_input_lowest: | 
|  | case hwmon_power_label: | 
|  | case hwmon_power_max_alarm: | 
|  | case hwmon_power_min_alarm: | 
|  | return 0444; | 
|  | case hwmon_power_reset_history: | 
|  | return 0200; | 
|  | case hwmon_power_max: | 
|  | case hwmon_power_min: | 
|  | return 0644; | 
|  | default: | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int ltc2947_temp_is_visible(const u32 attr) | 
|  | { | 
|  | switch (attr) { | 
|  | case hwmon_temp_input: | 
|  | case hwmon_temp_highest: | 
|  | case hwmon_temp_lowest: | 
|  | case hwmon_temp_max_alarm: | 
|  | case hwmon_temp_min_alarm: | 
|  | case hwmon_temp_label: | 
|  | return 0444; | 
|  | case hwmon_temp_reset_history: | 
|  | return 0200; | 
|  | case hwmon_temp_max: | 
|  | case hwmon_temp_min: | 
|  | return 0644; | 
|  | default: | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | static umode_t ltc2947_is_visible(const void *data, | 
|  | enum hwmon_sensor_types type, | 
|  | u32 attr, int channel) | 
|  | { | 
|  | switch (type) { | 
|  | case hwmon_in: | 
|  | return ltc2947_in_is_visible(attr); | 
|  | case hwmon_curr: | 
|  | return ltc2947_curr_is_visible(attr); | 
|  | case hwmon_power: | 
|  | return ltc2947_power_is_visible(attr); | 
|  | case hwmon_temp: | 
|  | return ltc2947_temp_is_visible(attr); | 
|  | default: | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | static const struct hwmon_channel_info *ltc2947_info[] = { | 
|  | HWMON_CHANNEL_INFO(in, | 
|  | HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | | 
|  | HWMON_I_MAX | HWMON_I_MIN | HWMON_I_RESET_HISTORY | | 
|  | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM | | 
|  | HWMON_I_LABEL, | 
|  | HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | | 
|  | HWMON_I_MAX | HWMON_I_MIN | HWMON_I_RESET_HISTORY | | 
|  | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM | | 
|  | HWMON_I_LABEL), | 
|  | HWMON_CHANNEL_INFO(curr, | 
|  | HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST | | 
|  | HWMON_C_MAX | HWMON_C_MIN | HWMON_C_RESET_HISTORY | | 
|  | HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM | | 
|  | HWMON_C_LABEL), | 
|  | HWMON_CHANNEL_INFO(power, | 
|  | HWMON_P_INPUT | HWMON_P_INPUT_LOWEST | | 
|  | HWMON_P_INPUT_HIGHEST | HWMON_P_MAX | HWMON_P_MIN | | 
|  | HWMON_P_RESET_HISTORY | HWMON_P_MAX_ALARM | | 
|  | HWMON_P_MIN_ALARM | HWMON_P_LABEL), | 
|  | HWMON_CHANNEL_INFO(temp, | 
|  | HWMON_T_INPUT | HWMON_T_LOWEST | HWMON_T_HIGHEST | | 
|  | HWMON_T_MAX | HWMON_T_MIN | HWMON_T_RESET_HISTORY | | 
|  | HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | | 
|  | HWMON_T_LABEL, | 
|  | HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM | HWMON_T_MAX | | 
|  | HWMON_T_MIN | HWMON_T_LABEL), | 
|  | NULL | 
|  | }; | 
|  |  | 
|  | static const struct hwmon_ops ltc2947_hwmon_ops = { | 
|  | .is_visible = ltc2947_is_visible, | 
|  | .read = ltc2947_read, | 
|  | .write = ltc2947_write, | 
|  | .read_string = ltc2947_read_labels, | 
|  | }; | 
|  |  | 
|  | static const struct hwmon_chip_info ltc2947_chip_info = { | 
|  | .ops = <c2947_hwmon_ops, | 
|  | .info = ltc2947_info, | 
|  | }; | 
|  |  | 
|  | /* energy attributes are 6bytes wide so we need u64 */ | 
|  | static SENSOR_DEVICE_ATTR(energy1_input, 0444, ltc2947_show_value, NULL, | 
|  | LTC2947_REG_ENERGY1); | 
|  | static SENSOR_DEVICE_ATTR(energy2_input, 0444, ltc2947_show_value, NULL, | 
|  | LTC2947_REG_ENERGY2); | 
|  |  | 
|  | static struct attribute *ltc2947_attrs[] = { | 
|  | &sensor_dev_attr_energy1_input.dev_attr.attr, | 
|  | &sensor_dev_attr_energy2_input.dev_attr.attr, | 
|  | NULL, | 
|  | }; | 
|  | ATTRIBUTE_GROUPS(ltc2947); | 
|  |  | 
|  | static void ltc2947_clk_disable(void *data) | 
|  | { | 
|  | struct clk *extclk = data; | 
|  |  | 
|  | clk_disable_unprepare(extclk); | 
|  | } | 
|  |  | 
|  | static int ltc2947_setup(struct ltc2947_data *st) | 
|  | { | 
|  | int ret; | 
|  | struct clk *extclk; | 
|  | u32 dummy, deadband, pol; | 
|  | u32 accum[2]; | 
|  |  | 
|  | /* clear status register by reading it */ | 
|  | ret = regmap_read(st->map, LTC2947_REG_STATUS, &dummy); | 
|  | if (ret) | 
|  | return ret; | 
|  | /* | 
|  | * Set max/min for power here since the default values x scale | 
|  | * would overflow on 32bit arch | 
|  | */ | 
|  | ret = ltc2947_val_write(st, LTC2947_REG_POWER_THRE_H, LTC2947_PAGE1, 2, | 
|  | POWER_MAX / 200000); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | ret = ltc2947_val_write(st, LTC2947_REG_POWER_THRE_L, LTC2947_PAGE1, 2, | 
|  | POWER_MIN / 200000); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | /* check external clock presence */ | 
|  | extclk = devm_clk_get_optional(st->dev, NULL); | 
|  | if (IS_ERR(extclk)) | 
|  | return dev_err_probe(st->dev, PTR_ERR(extclk), | 
|  | "Failed to get external clock\n"); | 
|  |  | 
|  | if (extclk) { | 
|  | unsigned long rate_hz; | 
|  | u8 pre = 0, div, tbctl; | 
|  | u64 aux; | 
|  |  | 
|  | /* let's calculate and set the right valus in TBCTL */ | 
|  | rate_hz = clk_get_rate(extclk); | 
|  | if (rate_hz < LTC2947_CLK_MIN || rate_hz > LTC2947_CLK_MAX) { | 
|  | dev_err(st->dev, "Invalid rate:%lu for external clock", | 
|  | rate_hz); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | ret = clk_prepare_enable(extclk); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | ret = devm_add_action_or_reset(st->dev, ltc2947_clk_disable, | 
|  | extclk); | 
|  | if (ret) | 
|  | return ret; | 
|  | /* as in table 1 of the datasheet */ | 
|  | if (rate_hz >= LTC2947_CLK_MIN && rate_hz <= 1000000) | 
|  | pre = 0; | 
|  | else if (rate_hz > 1000000 && rate_hz <= 2000000) | 
|  | pre = 1; | 
|  | else if (rate_hz > 2000000 && rate_hz <= 4000000) | 
|  | pre = 2; | 
|  | else if (rate_hz > 4000000 && rate_hz <= 8000000) | 
|  | pre = 3; | 
|  | else if (rate_hz > 8000000 && rate_hz <= 16000000) | 
|  | pre = 4; | 
|  | else if (rate_hz > 16000000 && rate_hz <= LTC2947_CLK_MAX) | 
|  | pre = 5; | 
|  | /* | 
|  | * Div is given by: | 
|  | *	floor(fref / (2^PRE * 32768)) | 
|  | */ | 
|  | div = rate_hz / ((1 << pre) * 32768); | 
|  | tbctl = LTC2947_PRE(pre) | LTC2947_DIV(div); | 
|  |  | 
|  | ret = regmap_write(st->map, LTC2947_REG_TBCTL, tbctl); | 
|  | if (ret) | 
|  | return ret; | 
|  | /* | 
|  | * The energy lsb is given by (in W*s): | 
|  | *      06416 * (1/fref) * 2^PRE * (DIV + 1) | 
|  | * The value is multiplied by 10E9 | 
|  | */ | 
|  | aux = (div + 1) * ((1 << pre) * 641600000ULL); | 
|  | st->lsb_energy = DIV_ROUND_CLOSEST_ULL(aux, rate_hz); | 
|  | } else { | 
|  | /* 19.89E-6 * 10E9 */ | 
|  | st->lsb_energy = 19890; | 
|  | } | 
|  | ret = of_property_read_u32_array(st->dev->of_node, | 
|  | "adi,accumulator-ctl-pol", accum, | 
|  | ARRAY_SIZE(accum)); | 
|  | if (!ret) { | 
|  | u32 accum_reg = LTC2947_ACCUM_POL_1(accum[0]) | | 
|  | LTC2947_ACCUM_POL_2(accum[1]); | 
|  |  | 
|  | ret = regmap_write(st->map, LTC2947_REG_ACCUM_POL, accum_reg); | 
|  | if (ret) | 
|  | return ret; | 
|  | } | 
|  | ret = of_property_read_u32(st->dev->of_node, | 
|  | "adi,accumulation-deadband-microamp", | 
|  | &deadband); | 
|  | if (!ret) { | 
|  | /* the LSB is the same as the current, so 3mA */ | 
|  | ret = regmap_write(st->map, LTC2947_REG_ACCUM_DEADBAND, | 
|  | deadband / (1000 * 3)); | 
|  | if (ret) | 
|  | return ret; | 
|  | } | 
|  | /* check gpio cfg */ | 
|  | ret = of_property_read_u32(st->dev->of_node, "adi,gpio-out-pol", &pol); | 
|  | if (!ret) { | 
|  | /* setup GPIO as output */ | 
|  | u32 gpio_ctl = LTC2947_GPIO_EN(1) | LTC2947_GPIO_FAN_EN(1) | | 
|  | LTC2947_GPIO_FAN_POL(pol); | 
|  |  | 
|  | st->gpio_out = true; | 
|  | ret = regmap_write(st->map, LTC2947_REG_GPIOSTATCTL, gpio_ctl); | 
|  | if (ret) | 
|  | return ret; | 
|  | } | 
|  | ret = of_property_read_u32_array(st->dev->of_node, "adi,gpio-in-accum", | 
|  | accum, ARRAY_SIZE(accum)); | 
|  | if (!ret) { | 
|  | /* | 
|  | * Setup the accum options. The gpioctl is already defined as | 
|  | * input by default. | 
|  | */ | 
|  | u32 accum_val = LTC2947_ACCUM_POL_1(accum[0]) | | 
|  | LTC2947_ACCUM_POL_2(accum[1]); | 
|  |  | 
|  | if (st->gpio_out) { | 
|  | dev_err(st->dev, | 
|  | "Cannot have input gpio config if already configured as output"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | ret = regmap_write(st->map, LTC2947_REG_GPIO_ACCUM, accum_val); | 
|  | if (ret) | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* set continuos mode */ | 
|  | return regmap_update_bits(st->map, LTC2947_REG_CTRL, | 
|  | LTC2947_CONT_MODE_MASK, LTC2947_CONT_MODE(1)); | 
|  | } | 
|  |  | 
|  | int ltc2947_core_probe(struct regmap *map, const char *name) | 
|  | { | 
|  | struct ltc2947_data *st; | 
|  | struct device *dev = regmap_get_device(map); | 
|  | struct device *hwmon; | 
|  | int ret; | 
|  |  | 
|  | st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); | 
|  | if (!st) | 
|  | return -ENOMEM; | 
|  |  | 
|  | st->map = map; | 
|  | st->dev = dev; | 
|  | dev_set_drvdata(dev, st); | 
|  | mutex_init(&st->lock); | 
|  |  | 
|  | ret = ltc2947_setup(st); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | hwmon = devm_hwmon_device_register_with_info(dev, name, st, | 
|  | <c2947_chip_info, | 
|  | ltc2947_groups); | 
|  | return PTR_ERR_OR_ZERO(hwmon); | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(ltc2947_core_probe); | 
|  |  | 
|  | static int __maybe_unused ltc2947_resume(struct device *dev) | 
|  | { | 
|  | struct ltc2947_data *st = dev_get_drvdata(dev); | 
|  | u32 ctrl = 0; | 
|  | int ret; | 
|  |  | 
|  | /* dummy read to wake the device */ | 
|  | ret = regmap_read(st->map, LTC2947_REG_CTRL, &ctrl); | 
|  | if (ret) | 
|  | return ret; | 
|  | /* | 
|  | * Wait for the device. It takes 100ms to wake up so, 10ms extra | 
|  | * should be enough. | 
|  | */ | 
|  | msleep(110); | 
|  | ret = regmap_read(st->map, LTC2947_REG_CTRL, &ctrl); | 
|  | if (ret) | 
|  | return ret; | 
|  | /* ctrl should be 0 */ | 
|  | if (ctrl != 0) { | 
|  | dev_err(st->dev, "Device failed to wake up, ctl:%02X\n", ctrl); | 
|  | return -ETIMEDOUT; | 
|  | } | 
|  |  | 
|  | /* set continuous mode */ | 
|  | return regmap_update_bits(st->map, LTC2947_REG_CTRL, | 
|  | LTC2947_CONT_MODE_MASK, LTC2947_CONT_MODE(1)); | 
|  | } | 
|  |  | 
|  | static int __maybe_unused ltc2947_suspend(struct device *dev) | 
|  | { | 
|  | struct ltc2947_data *st = dev_get_drvdata(dev); | 
|  |  | 
|  | return regmap_update_bits(st->map, LTC2947_REG_CTRL, | 
|  | LTC2947_SHUTDOWN_MASK, 1); | 
|  | } | 
|  |  | 
|  | SIMPLE_DEV_PM_OPS(ltc2947_pm_ops, ltc2947_suspend, ltc2947_resume); | 
|  | EXPORT_SYMBOL_GPL(ltc2947_pm_ops); | 
|  |  | 
|  | const struct of_device_id ltc2947_of_match[] = { | 
|  | { .compatible = "adi,ltc2947" }, | 
|  | {} | 
|  | }; | 
|  | EXPORT_SYMBOL_GPL(ltc2947_of_match); | 
|  | MODULE_DEVICE_TABLE(of, ltc2947_of_match); | 
|  |  | 
|  | MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>"); | 
|  | MODULE_DESCRIPTION("LTC2947 power and energy monitor core driver"); | 
|  | MODULE_LICENSE("GPL"); |