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 = <c2947_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 <c2947_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