18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Linear Technology LTC2945 I2C Power Monitor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2014 Guenter Roeck 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/i2c.h> 138c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 148c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 158c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 168c2ecf20Sopenharmony_ci#include <linux/regmap.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* chip registers */ 198c2ecf20Sopenharmony_ci#define LTC2945_CONTROL 0x00 208c2ecf20Sopenharmony_ci#define LTC2945_ALERT 0x01 218c2ecf20Sopenharmony_ci#define LTC2945_STATUS 0x02 228c2ecf20Sopenharmony_ci#define LTC2945_FAULT 0x03 238c2ecf20Sopenharmony_ci#define LTC2945_POWER_H 0x05 248c2ecf20Sopenharmony_ci#define LTC2945_MAX_POWER_H 0x08 258c2ecf20Sopenharmony_ci#define LTC2945_MIN_POWER_H 0x0b 268c2ecf20Sopenharmony_ci#define LTC2945_MAX_POWER_THRES_H 0x0e 278c2ecf20Sopenharmony_ci#define LTC2945_MIN_POWER_THRES_H 0x11 288c2ecf20Sopenharmony_ci#define LTC2945_SENSE_H 0x14 298c2ecf20Sopenharmony_ci#define LTC2945_MAX_SENSE_H 0x16 308c2ecf20Sopenharmony_ci#define LTC2945_MIN_SENSE_H 0x18 318c2ecf20Sopenharmony_ci#define LTC2945_MAX_SENSE_THRES_H 0x1a 328c2ecf20Sopenharmony_ci#define LTC2945_MIN_SENSE_THRES_H 0x1c 338c2ecf20Sopenharmony_ci#define LTC2945_VIN_H 0x1e 348c2ecf20Sopenharmony_ci#define LTC2945_MAX_VIN_H 0x20 358c2ecf20Sopenharmony_ci#define LTC2945_MIN_VIN_H 0x22 368c2ecf20Sopenharmony_ci#define LTC2945_MAX_VIN_THRES_H 0x24 378c2ecf20Sopenharmony_ci#define LTC2945_MIN_VIN_THRES_H 0x26 388c2ecf20Sopenharmony_ci#define LTC2945_ADIN_H 0x28 398c2ecf20Sopenharmony_ci#define LTC2945_MAX_ADIN_H 0x2a 408c2ecf20Sopenharmony_ci#define LTC2945_MIN_ADIN_H 0x2c 418c2ecf20Sopenharmony_ci#define LTC2945_MAX_ADIN_THRES_H 0x2e 428c2ecf20Sopenharmony_ci#define LTC2945_MIN_ADIN_THRES_H 0x30 438c2ecf20Sopenharmony_ci#define LTC2945_MIN_ADIN_THRES_L 0x31 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Fault register bits */ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define FAULT_ADIN_UV (1 << 0) 488c2ecf20Sopenharmony_ci#define FAULT_ADIN_OV (1 << 1) 498c2ecf20Sopenharmony_ci#define FAULT_VIN_UV (1 << 2) 508c2ecf20Sopenharmony_ci#define FAULT_VIN_OV (1 << 3) 518c2ecf20Sopenharmony_ci#define FAULT_SENSE_UV (1 << 4) 528c2ecf20Sopenharmony_ci#define FAULT_SENSE_OV (1 << 5) 538c2ecf20Sopenharmony_ci#define FAULT_POWER_UV (1 << 6) 548c2ecf20Sopenharmony_ci#define FAULT_POWER_OV (1 << 7) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* Control register bits */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define CONTROL_MULT_SELECT (1 << 0) 598c2ecf20Sopenharmony_ci#define CONTROL_TEST_MODE (1 << 4) 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic inline bool is_power_reg(u8 reg) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci return reg < LTC2945_SENSE_H; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* Return the value from the given register in uW, mV, or mA */ 678c2ecf20Sopenharmony_cistatic long long ltc2945_reg_to_val(struct device *dev, u8 reg) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct regmap *regmap = dev_get_drvdata(dev); 708c2ecf20Sopenharmony_ci unsigned int control; 718c2ecf20Sopenharmony_ci u8 buf[3]; 728c2ecf20Sopenharmony_ci long long val; 738c2ecf20Sopenharmony_ci int ret; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci ret = regmap_bulk_read(regmap, reg, buf, 768c2ecf20Sopenharmony_ci is_power_reg(reg) ? 3 : 2); 778c2ecf20Sopenharmony_ci if (ret < 0) 788c2ecf20Sopenharmony_ci return ret; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (is_power_reg(reg)) { 818c2ecf20Sopenharmony_ci /* power */ 828c2ecf20Sopenharmony_ci val = (buf[0] << 16) + (buf[1] << 8) + buf[2]; 838c2ecf20Sopenharmony_ci } else { 848c2ecf20Sopenharmony_ci /* current, voltage */ 858c2ecf20Sopenharmony_ci val = (buf[0] << 4) + (buf[1] >> 4); 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci switch (reg) { 898c2ecf20Sopenharmony_ci case LTC2945_POWER_H: 908c2ecf20Sopenharmony_ci case LTC2945_MAX_POWER_H: 918c2ecf20Sopenharmony_ci case LTC2945_MIN_POWER_H: 928c2ecf20Sopenharmony_ci case LTC2945_MAX_POWER_THRES_H: 938c2ecf20Sopenharmony_ci case LTC2945_MIN_POWER_THRES_H: 948c2ecf20Sopenharmony_ci /* 958c2ecf20Sopenharmony_ci * Convert to uW by assuming current is measured with 968c2ecf20Sopenharmony_ci * an 1mOhm sense resistor, similar to current 978c2ecf20Sopenharmony_ci * measurements. 988c2ecf20Sopenharmony_ci * Control register bit 0 selects if voltage at SENSE+/VDD 998c2ecf20Sopenharmony_ci * or voltage at ADIN is used to measure power. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci ret = regmap_read(regmap, LTC2945_CONTROL, &control); 1028c2ecf20Sopenharmony_ci if (ret < 0) 1038c2ecf20Sopenharmony_ci return ret; 1048c2ecf20Sopenharmony_ci if (control & CONTROL_MULT_SELECT) { 1058c2ecf20Sopenharmony_ci /* 25 mV * 25 uV = 0.625 uV resolution. */ 1068c2ecf20Sopenharmony_ci val *= 625LL; 1078c2ecf20Sopenharmony_ci } else { 1088c2ecf20Sopenharmony_ci /* 0.5 mV * 25 uV = 0.0125 uV resolution. */ 1098c2ecf20Sopenharmony_ci val = (val * 25LL) >> 1; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci break; 1128c2ecf20Sopenharmony_ci case LTC2945_VIN_H: 1138c2ecf20Sopenharmony_ci case LTC2945_MAX_VIN_H: 1148c2ecf20Sopenharmony_ci case LTC2945_MIN_VIN_H: 1158c2ecf20Sopenharmony_ci case LTC2945_MAX_VIN_THRES_H: 1168c2ecf20Sopenharmony_ci case LTC2945_MIN_VIN_THRES_H: 1178c2ecf20Sopenharmony_ci /* 25 mV resolution. Convert to mV. */ 1188c2ecf20Sopenharmony_ci val *= 25; 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci case LTC2945_ADIN_H: 1218c2ecf20Sopenharmony_ci case LTC2945_MAX_ADIN_H: 1228c2ecf20Sopenharmony_ci case LTC2945_MIN_ADIN_THRES_H: 1238c2ecf20Sopenharmony_ci case LTC2945_MAX_ADIN_THRES_H: 1248c2ecf20Sopenharmony_ci case LTC2945_MIN_ADIN_H: 1258c2ecf20Sopenharmony_ci /* 0.5mV resolution. Convert to mV. */ 1268c2ecf20Sopenharmony_ci val = val >> 1; 1278c2ecf20Sopenharmony_ci break; 1288c2ecf20Sopenharmony_ci case LTC2945_SENSE_H: 1298c2ecf20Sopenharmony_ci case LTC2945_MAX_SENSE_H: 1308c2ecf20Sopenharmony_ci case LTC2945_MIN_SENSE_H: 1318c2ecf20Sopenharmony_ci case LTC2945_MAX_SENSE_THRES_H: 1328c2ecf20Sopenharmony_ci case LTC2945_MIN_SENSE_THRES_H: 1338c2ecf20Sopenharmony_ci /* 1348c2ecf20Sopenharmony_ci * 25 uV resolution. Convert to current as measured with 1358c2ecf20Sopenharmony_ci * an 1 mOhm sense resistor, in mA. If a different sense 1368c2ecf20Sopenharmony_ci * resistor is installed, calculate the actual current by 1378c2ecf20Sopenharmony_ci * dividing the reported current by the sense resistor value 1388c2ecf20Sopenharmony_ci * in mOhm. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci val *= 25; 1418c2ecf20Sopenharmony_ci break; 1428c2ecf20Sopenharmony_ci default: 1438c2ecf20Sopenharmony_ci return -EINVAL; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci return val; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int ltc2945_val_to_reg(struct device *dev, u8 reg, 1498c2ecf20Sopenharmony_ci unsigned long val) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct regmap *regmap = dev_get_drvdata(dev); 1528c2ecf20Sopenharmony_ci unsigned int control; 1538c2ecf20Sopenharmony_ci int ret; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci switch (reg) { 1568c2ecf20Sopenharmony_ci case LTC2945_POWER_H: 1578c2ecf20Sopenharmony_ci case LTC2945_MAX_POWER_H: 1588c2ecf20Sopenharmony_ci case LTC2945_MIN_POWER_H: 1598c2ecf20Sopenharmony_ci case LTC2945_MAX_POWER_THRES_H: 1608c2ecf20Sopenharmony_ci case LTC2945_MIN_POWER_THRES_H: 1618c2ecf20Sopenharmony_ci /* 1628c2ecf20Sopenharmony_ci * Convert to register value by assuming current is measured 1638c2ecf20Sopenharmony_ci * with an 1mOhm sense resistor, similar to current 1648c2ecf20Sopenharmony_ci * measurements. 1658c2ecf20Sopenharmony_ci * Control register bit 0 selects if voltage at SENSE+/VDD 1668c2ecf20Sopenharmony_ci * or voltage at ADIN is used to measure power, which in turn 1678c2ecf20Sopenharmony_ci * determines register calculations. 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_ci ret = regmap_read(regmap, LTC2945_CONTROL, &control); 1708c2ecf20Sopenharmony_ci if (ret < 0) 1718c2ecf20Sopenharmony_ci return ret; 1728c2ecf20Sopenharmony_ci if (control & CONTROL_MULT_SELECT) { 1738c2ecf20Sopenharmony_ci /* 25 mV * 25 uV = 0.625 uV resolution. */ 1748c2ecf20Sopenharmony_ci val = DIV_ROUND_CLOSEST(val, 625); 1758c2ecf20Sopenharmony_ci } else { 1768c2ecf20Sopenharmony_ci /* 1778c2ecf20Sopenharmony_ci * 0.5 mV * 25 uV = 0.0125 uV resolution. 1788c2ecf20Sopenharmony_ci * Divide first to avoid overflow; 1798c2ecf20Sopenharmony_ci * accept loss of accuracy. 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci val = DIV_ROUND_CLOSEST(val, 25) * 2; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci case LTC2945_VIN_H: 1858c2ecf20Sopenharmony_ci case LTC2945_MAX_VIN_H: 1868c2ecf20Sopenharmony_ci case LTC2945_MIN_VIN_H: 1878c2ecf20Sopenharmony_ci case LTC2945_MAX_VIN_THRES_H: 1888c2ecf20Sopenharmony_ci case LTC2945_MIN_VIN_THRES_H: 1898c2ecf20Sopenharmony_ci /* 25 mV resolution. */ 1908c2ecf20Sopenharmony_ci val /= 25; 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci case LTC2945_ADIN_H: 1938c2ecf20Sopenharmony_ci case LTC2945_MAX_ADIN_H: 1948c2ecf20Sopenharmony_ci case LTC2945_MIN_ADIN_THRES_H: 1958c2ecf20Sopenharmony_ci case LTC2945_MAX_ADIN_THRES_H: 1968c2ecf20Sopenharmony_ci case LTC2945_MIN_ADIN_H: 1978c2ecf20Sopenharmony_ci /* 0.5mV resolution. */ 1988c2ecf20Sopenharmony_ci val *= 2; 1998c2ecf20Sopenharmony_ci break; 2008c2ecf20Sopenharmony_ci case LTC2945_SENSE_H: 2018c2ecf20Sopenharmony_ci case LTC2945_MAX_SENSE_H: 2028c2ecf20Sopenharmony_ci case LTC2945_MIN_SENSE_H: 2038c2ecf20Sopenharmony_ci case LTC2945_MAX_SENSE_THRES_H: 2048c2ecf20Sopenharmony_ci case LTC2945_MIN_SENSE_THRES_H: 2058c2ecf20Sopenharmony_ci /* 2068c2ecf20Sopenharmony_ci * 25 uV resolution. Convert to current as measured with 2078c2ecf20Sopenharmony_ci * an 1 mOhm sense resistor, in mA. If a different sense 2088c2ecf20Sopenharmony_ci * resistor is installed, calculate the actual current by 2098c2ecf20Sopenharmony_ci * dividing the reported current by the sense resistor value 2108c2ecf20Sopenharmony_ci * in mOhm. 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ci val = DIV_ROUND_CLOSEST(val, 25); 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci default: 2158c2ecf20Sopenharmony_ci return -EINVAL; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci return val; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic ssize_t ltc2945_value_show(struct device *dev, 2218c2ecf20Sopenharmony_ci struct device_attribute *da, char *buf) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 2248c2ecf20Sopenharmony_ci long long value; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci value = ltc2945_reg_to_val(dev, attr->index); 2278c2ecf20Sopenharmony_ci if (value < 0) 2288c2ecf20Sopenharmony_ci return value; 2298c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%lld\n", value); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic ssize_t ltc2945_value_store(struct device *dev, 2338c2ecf20Sopenharmony_ci struct device_attribute *da, 2348c2ecf20Sopenharmony_ci const char *buf, size_t count) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 2378c2ecf20Sopenharmony_ci struct regmap *regmap = dev_get_drvdata(dev); 2388c2ecf20Sopenharmony_ci u8 reg = attr->index; 2398c2ecf20Sopenharmony_ci unsigned long val; 2408c2ecf20Sopenharmony_ci u8 regbuf[3]; 2418c2ecf20Sopenharmony_ci int num_regs; 2428c2ecf20Sopenharmony_ci int regval; 2438c2ecf20Sopenharmony_ci int ret; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 10, &val); 2468c2ecf20Sopenharmony_ci if (ret) 2478c2ecf20Sopenharmony_ci return ret; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* convert to register value, then clamp and write result */ 2508c2ecf20Sopenharmony_ci regval = ltc2945_val_to_reg(dev, reg, val); 2518c2ecf20Sopenharmony_ci if (regval < 0) 2528c2ecf20Sopenharmony_ci return regval; 2538c2ecf20Sopenharmony_ci if (is_power_reg(reg)) { 2548c2ecf20Sopenharmony_ci regval = clamp_val(regval, 0, 0xffffff); 2558c2ecf20Sopenharmony_ci regbuf[0] = regval >> 16; 2568c2ecf20Sopenharmony_ci regbuf[1] = (regval >> 8) & 0xff; 2578c2ecf20Sopenharmony_ci regbuf[2] = regval; 2588c2ecf20Sopenharmony_ci num_regs = 3; 2598c2ecf20Sopenharmony_ci } else { 2608c2ecf20Sopenharmony_ci regval = clamp_val(regval, 0, 0xfff) << 4; 2618c2ecf20Sopenharmony_ci regbuf[0] = regval >> 8; 2628c2ecf20Sopenharmony_ci regbuf[1] = regval & 0xff; 2638c2ecf20Sopenharmony_ci num_regs = 2; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci ret = regmap_bulk_write(regmap, reg, regbuf, num_regs); 2668c2ecf20Sopenharmony_ci return ret < 0 ? ret : count; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic ssize_t ltc2945_history_store(struct device *dev, 2708c2ecf20Sopenharmony_ci struct device_attribute *da, 2718c2ecf20Sopenharmony_ci const char *buf, size_t count) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 2748c2ecf20Sopenharmony_ci struct regmap *regmap = dev_get_drvdata(dev); 2758c2ecf20Sopenharmony_ci u8 reg = attr->index; 2768c2ecf20Sopenharmony_ci int num_regs = is_power_reg(reg) ? 3 : 2; 2778c2ecf20Sopenharmony_ci u8 buf_min[3] = { 0xff, 0xff, 0xff }; 2788c2ecf20Sopenharmony_ci u8 buf_max[3] = { 0, 0, 0 }; 2798c2ecf20Sopenharmony_ci unsigned long val; 2808c2ecf20Sopenharmony_ci int ret; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 10, &val); 2838c2ecf20Sopenharmony_ci if (ret) 2848c2ecf20Sopenharmony_ci return ret; 2858c2ecf20Sopenharmony_ci if (val != 1) 2868c2ecf20Sopenharmony_ci return -EINVAL; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ret = regmap_update_bits(regmap, LTC2945_CONTROL, CONTROL_TEST_MODE, 2898c2ecf20Sopenharmony_ci CONTROL_TEST_MODE); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* Reset minimum */ 2928c2ecf20Sopenharmony_ci ret = regmap_bulk_write(regmap, reg, buf_min, num_regs); 2938c2ecf20Sopenharmony_ci if (ret) 2948c2ecf20Sopenharmony_ci return ret; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci switch (reg) { 2978c2ecf20Sopenharmony_ci case LTC2945_MIN_POWER_H: 2988c2ecf20Sopenharmony_ci reg = LTC2945_MAX_POWER_H; 2998c2ecf20Sopenharmony_ci break; 3008c2ecf20Sopenharmony_ci case LTC2945_MIN_SENSE_H: 3018c2ecf20Sopenharmony_ci reg = LTC2945_MAX_SENSE_H; 3028c2ecf20Sopenharmony_ci break; 3038c2ecf20Sopenharmony_ci case LTC2945_MIN_VIN_H: 3048c2ecf20Sopenharmony_ci reg = LTC2945_MAX_VIN_H; 3058c2ecf20Sopenharmony_ci break; 3068c2ecf20Sopenharmony_ci case LTC2945_MIN_ADIN_H: 3078c2ecf20Sopenharmony_ci reg = LTC2945_MAX_ADIN_H; 3088c2ecf20Sopenharmony_ci break; 3098c2ecf20Sopenharmony_ci default: 3108c2ecf20Sopenharmony_ci WARN_ONCE(1, "Bad register: 0x%x\n", reg); 3118c2ecf20Sopenharmony_ci return -EINVAL; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci /* Reset maximum */ 3148c2ecf20Sopenharmony_ci ret = regmap_bulk_write(regmap, reg, buf_max, num_regs); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* Try resetting test mode even if there was an error */ 3178c2ecf20Sopenharmony_ci regmap_update_bits(regmap, LTC2945_CONTROL, CONTROL_TEST_MODE, 0); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return ret ? : count; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic ssize_t ltc2945_bool_show(struct device *dev, 3238c2ecf20Sopenharmony_ci struct device_attribute *da, char *buf) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 3268c2ecf20Sopenharmony_ci struct regmap *regmap = dev_get_drvdata(dev); 3278c2ecf20Sopenharmony_ci unsigned int fault; 3288c2ecf20Sopenharmony_ci int ret; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci ret = regmap_read(regmap, LTC2945_FAULT, &fault); 3318c2ecf20Sopenharmony_ci if (ret < 0) 3328c2ecf20Sopenharmony_ci return ret; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci fault &= attr->index; 3358c2ecf20Sopenharmony_ci if (fault) /* Clear reported faults in chip register */ 3368c2ecf20Sopenharmony_ci regmap_update_bits(regmap, LTC2945_FAULT, attr->index, 0); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci/* Input voltages */ 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_input, ltc2945_value, LTC2945_VIN_H); 3448c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in1_min, ltc2945_value, LTC2945_MIN_VIN_THRES_H); 3458c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in1_max, ltc2945_value, LTC2945_MAX_VIN_THRES_H); 3468c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_lowest, ltc2945_value, LTC2945_MIN_VIN_H); 3478c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_highest, ltc2945_value, LTC2945_MAX_VIN_H); 3488c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_WO(in1_reset_history, ltc2945_history, 3498c2ecf20Sopenharmony_ci LTC2945_MIN_VIN_H); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in2_input, ltc2945_value, LTC2945_ADIN_H); 3528c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in2_min, ltc2945_value, LTC2945_MIN_ADIN_THRES_H); 3538c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in2_max, ltc2945_value, LTC2945_MAX_ADIN_THRES_H); 3548c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in2_lowest, ltc2945_value, LTC2945_MIN_ADIN_H); 3558c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in2_highest, ltc2945_value, LTC2945_MAX_ADIN_H); 3568c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_WO(in2_reset_history, ltc2945_history, 3578c2ecf20Sopenharmony_ci LTC2945_MIN_ADIN_H); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/* Voltage alarms */ 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_min_alarm, ltc2945_bool, FAULT_VIN_UV); 3628c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_max_alarm, ltc2945_bool, FAULT_VIN_OV); 3638c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in2_min_alarm, ltc2945_bool, FAULT_ADIN_UV); 3648c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in2_max_alarm, ltc2945_bool, FAULT_ADIN_OV); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci/* Currents (via sense resistor) */ 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(curr1_input, ltc2945_value, LTC2945_SENSE_H); 3698c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(curr1_min, ltc2945_value, 3708c2ecf20Sopenharmony_ci LTC2945_MIN_SENSE_THRES_H); 3718c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(curr1_max, ltc2945_value, 3728c2ecf20Sopenharmony_ci LTC2945_MAX_SENSE_THRES_H); 3738c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(curr1_lowest, ltc2945_value, LTC2945_MIN_SENSE_H); 3748c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(curr1_highest, ltc2945_value, 3758c2ecf20Sopenharmony_ci LTC2945_MAX_SENSE_H); 3768c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_WO(curr1_reset_history, ltc2945_history, 3778c2ecf20Sopenharmony_ci LTC2945_MIN_SENSE_H); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci/* Current alarms */ 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(curr1_min_alarm, ltc2945_bool, FAULT_SENSE_UV); 3828c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(curr1_max_alarm, ltc2945_bool, FAULT_SENSE_OV); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci/* Power */ 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(power1_input, ltc2945_value, LTC2945_POWER_H); 3878c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(power1_min, ltc2945_value, 3888c2ecf20Sopenharmony_ci LTC2945_MIN_POWER_THRES_H); 3898c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(power1_max, ltc2945_value, 3908c2ecf20Sopenharmony_ci LTC2945_MAX_POWER_THRES_H); 3918c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(power1_input_lowest, ltc2945_value, 3928c2ecf20Sopenharmony_ci LTC2945_MIN_POWER_H); 3938c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(power1_input_highest, ltc2945_value, 3948c2ecf20Sopenharmony_ci LTC2945_MAX_POWER_H); 3958c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_WO(power1_reset_history, ltc2945_history, 3968c2ecf20Sopenharmony_ci LTC2945_MIN_POWER_H); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci/* Power alarms */ 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(power1_min_alarm, ltc2945_bool, FAULT_POWER_UV); 4018c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(power1_max_alarm, ltc2945_bool, FAULT_POWER_OV); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic struct attribute *ltc2945_attrs[] = { 4048c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_input.dev_attr.attr, 4058c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_min.dev_attr.attr, 4068c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_max.dev_attr.attr, 4078c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_lowest.dev_attr.attr, 4088c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_highest.dev_attr.attr, 4098c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_reset_history.dev_attr.attr, 4108c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_min_alarm.dev_attr.attr, 4118c2ecf20Sopenharmony_ci &sensor_dev_attr_in1_max_alarm.dev_attr.attr, 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_input.dev_attr.attr, 4148c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_min.dev_attr.attr, 4158c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_max.dev_attr.attr, 4168c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_lowest.dev_attr.attr, 4178c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_highest.dev_attr.attr, 4188c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_reset_history.dev_attr.attr, 4198c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_min_alarm.dev_attr.attr, 4208c2ecf20Sopenharmony_ci &sensor_dev_attr_in2_max_alarm.dev_attr.attr, 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci &sensor_dev_attr_curr1_input.dev_attr.attr, 4238c2ecf20Sopenharmony_ci &sensor_dev_attr_curr1_min.dev_attr.attr, 4248c2ecf20Sopenharmony_ci &sensor_dev_attr_curr1_max.dev_attr.attr, 4258c2ecf20Sopenharmony_ci &sensor_dev_attr_curr1_lowest.dev_attr.attr, 4268c2ecf20Sopenharmony_ci &sensor_dev_attr_curr1_highest.dev_attr.attr, 4278c2ecf20Sopenharmony_ci &sensor_dev_attr_curr1_reset_history.dev_attr.attr, 4288c2ecf20Sopenharmony_ci &sensor_dev_attr_curr1_min_alarm.dev_attr.attr, 4298c2ecf20Sopenharmony_ci &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci &sensor_dev_attr_power1_input.dev_attr.attr, 4328c2ecf20Sopenharmony_ci &sensor_dev_attr_power1_min.dev_attr.attr, 4338c2ecf20Sopenharmony_ci &sensor_dev_attr_power1_max.dev_attr.attr, 4348c2ecf20Sopenharmony_ci &sensor_dev_attr_power1_input_lowest.dev_attr.attr, 4358c2ecf20Sopenharmony_ci &sensor_dev_attr_power1_input_highest.dev_attr.attr, 4368c2ecf20Sopenharmony_ci &sensor_dev_attr_power1_reset_history.dev_attr.attr, 4378c2ecf20Sopenharmony_ci &sensor_dev_attr_power1_min_alarm.dev_attr.attr, 4388c2ecf20Sopenharmony_ci &sensor_dev_attr_power1_max_alarm.dev_attr.attr, 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci NULL, 4418c2ecf20Sopenharmony_ci}; 4428c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(ltc2945); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic const struct regmap_config ltc2945_regmap_config = { 4458c2ecf20Sopenharmony_ci .reg_bits = 8, 4468c2ecf20Sopenharmony_ci .val_bits = 8, 4478c2ecf20Sopenharmony_ci .max_register = LTC2945_MIN_ADIN_THRES_L, 4488c2ecf20Sopenharmony_ci}; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic int ltc2945_probe(struct i2c_client *client) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 4538c2ecf20Sopenharmony_ci struct device *hwmon_dev; 4548c2ecf20Sopenharmony_ci struct regmap *regmap; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci regmap = devm_regmap_init_i2c(client, <c2945_regmap_config); 4578c2ecf20Sopenharmony_ci if (IS_ERR(regmap)) { 4588c2ecf20Sopenharmony_ci dev_err(dev, "failed to allocate register map\n"); 4598c2ecf20Sopenharmony_ci return PTR_ERR(regmap); 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* Clear faults */ 4638c2ecf20Sopenharmony_ci regmap_write(regmap, LTC2945_FAULT, 0x00); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, 4668c2ecf20Sopenharmony_ci regmap, 4678c2ecf20Sopenharmony_ci ltc2945_groups); 4688c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(hwmon_dev); 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic const struct i2c_device_id ltc2945_id[] = { 4728c2ecf20Sopenharmony_ci {"ltc2945", 0}, 4738c2ecf20Sopenharmony_ci { } 4748c2ecf20Sopenharmony_ci}; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ltc2945_id); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cistatic struct i2c_driver ltc2945_driver = { 4798c2ecf20Sopenharmony_ci .driver = { 4808c2ecf20Sopenharmony_ci .name = "ltc2945", 4818c2ecf20Sopenharmony_ci }, 4828c2ecf20Sopenharmony_ci .probe_new = ltc2945_probe, 4838c2ecf20Sopenharmony_ci .id_table = ltc2945_id, 4848c2ecf20Sopenharmony_ci}; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cimodule_i2c_driver(ltc2945_driver); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ciMODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); 4898c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("LTC2945 driver"); 4908c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 491