162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for the Texas Instruments / Burr Brown INA209 462306a36Sopenharmony_ci * Bidirectional Current/Power Monitor 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2012 Guenter Roeck <linux@roeck-us.net> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Derived from Ira W. Snyder's original driver submission 962306a36Sopenharmony_ci * Copyright (C) 2008 Paul Hays <Paul.Hays@cattail.ca> 1062306a36Sopenharmony_ci * Copyright (C) 2008-2009 Ira W. Snyder <iws@ovro.caltech.edu> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Aligned with ina2xx driver 1362306a36Sopenharmony_ci * Copyright (C) 2012 Lothar Felten <l-felten@ti.com> 1462306a36Sopenharmony_ci * Thanks to Jan Volkering 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Datasheet: 1762306a36Sopenharmony_ci * https://www.ti.com/lit/gpn/ina209 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/kernel.h> 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/init.h> 2362306a36Sopenharmony_ci#include <linux/err.h> 2462306a36Sopenharmony_ci#include <linux/slab.h> 2562306a36Sopenharmony_ci#include <linux/bug.h> 2662306a36Sopenharmony_ci#include <linux/i2c.h> 2762306a36Sopenharmony_ci#include <linux/hwmon.h> 2862306a36Sopenharmony_ci#include <linux/hwmon-sysfs.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <linux/platform_data/ina2xx.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* register definitions */ 3362306a36Sopenharmony_ci#define INA209_CONFIGURATION 0x00 3462306a36Sopenharmony_ci#define INA209_STATUS 0x01 3562306a36Sopenharmony_ci#define INA209_STATUS_MASK 0x02 3662306a36Sopenharmony_ci#define INA209_SHUNT_VOLTAGE 0x03 3762306a36Sopenharmony_ci#define INA209_BUS_VOLTAGE 0x04 3862306a36Sopenharmony_ci#define INA209_POWER 0x05 3962306a36Sopenharmony_ci#define INA209_CURRENT 0x06 4062306a36Sopenharmony_ci#define INA209_SHUNT_VOLTAGE_POS_PEAK 0x07 4162306a36Sopenharmony_ci#define INA209_SHUNT_VOLTAGE_NEG_PEAK 0x08 4262306a36Sopenharmony_ci#define INA209_BUS_VOLTAGE_MAX_PEAK 0x09 4362306a36Sopenharmony_ci#define INA209_BUS_VOLTAGE_MIN_PEAK 0x0a 4462306a36Sopenharmony_ci#define INA209_POWER_PEAK 0x0b 4562306a36Sopenharmony_ci#define INA209_SHUNT_VOLTAGE_POS_WARN 0x0c 4662306a36Sopenharmony_ci#define INA209_SHUNT_VOLTAGE_NEG_WARN 0x0d 4762306a36Sopenharmony_ci#define INA209_POWER_WARN 0x0e 4862306a36Sopenharmony_ci#define INA209_BUS_VOLTAGE_OVER_WARN 0x0f 4962306a36Sopenharmony_ci#define INA209_BUS_VOLTAGE_UNDER_WARN 0x10 5062306a36Sopenharmony_ci#define INA209_POWER_OVER_LIMIT 0x11 5162306a36Sopenharmony_ci#define INA209_BUS_VOLTAGE_OVER_LIMIT 0x12 5262306a36Sopenharmony_ci#define INA209_BUS_VOLTAGE_UNDER_LIMIT 0x13 5362306a36Sopenharmony_ci#define INA209_CRITICAL_DAC_POS 0x14 5462306a36Sopenharmony_ci#define INA209_CRITICAL_DAC_NEG 0x15 5562306a36Sopenharmony_ci#define INA209_CALIBRATION 0x16 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define INA209_REGISTERS 0x17 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define INA209_CONFIG_DEFAULT 0x3c47 /* PGA=8, full range */ 6062306a36Sopenharmony_ci#define INA209_SHUNT_DEFAULT 10000 /* uOhm */ 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistruct ina209_data { 6362306a36Sopenharmony_ci struct i2c_client *client; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci struct mutex update_lock; 6662306a36Sopenharmony_ci bool valid; 6762306a36Sopenharmony_ci unsigned long last_updated; /* in jiffies */ 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci u16 regs[INA209_REGISTERS]; /* All chip registers */ 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci u16 config_orig; /* Original configuration */ 7262306a36Sopenharmony_ci u16 calibration_orig; /* Original calibration */ 7362306a36Sopenharmony_ci u16 update_interval; 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic struct ina209_data *ina209_update_device(struct device *dev) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct ina209_data *data = dev_get_drvdata(dev); 7962306a36Sopenharmony_ci struct i2c_client *client = data->client; 8062306a36Sopenharmony_ci struct ina209_data *ret = data; 8162306a36Sopenharmony_ci s32 val; 8262306a36Sopenharmony_ci int i; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci mutex_lock(&data->update_lock); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (!data->valid || 8762306a36Sopenharmony_ci time_after(jiffies, data->last_updated + data->update_interval)) { 8862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data->regs); i++) { 8962306a36Sopenharmony_ci val = i2c_smbus_read_word_swapped(client, i); 9062306a36Sopenharmony_ci if (val < 0) { 9162306a36Sopenharmony_ci ret = ERR_PTR(val); 9262306a36Sopenharmony_ci goto abort; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci data->regs[i] = val; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci data->last_updated = jiffies; 9762306a36Sopenharmony_ci data->valid = true; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ciabort: 10062306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 10162306a36Sopenharmony_ci return ret; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* 10562306a36Sopenharmony_ci * Read a value from a device register and convert it to the 10662306a36Sopenharmony_ci * appropriate sysfs units 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_cistatic long ina209_from_reg(const u8 reg, const u16 val) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci switch (reg) { 11162306a36Sopenharmony_ci case INA209_SHUNT_VOLTAGE: 11262306a36Sopenharmony_ci case INA209_SHUNT_VOLTAGE_POS_PEAK: 11362306a36Sopenharmony_ci case INA209_SHUNT_VOLTAGE_NEG_PEAK: 11462306a36Sopenharmony_ci case INA209_SHUNT_VOLTAGE_POS_WARN: 11562306a36Sopenharmony_ci case INA209_SHUNT_VOLTAGE_NEG_WARN: 11662306a36Sopenharmony_ci /* LSB=10 uV. Convert to mV. */ 11762306a36Sopenharmony_ci return DIV_ROUND_CLOSEST((s16)val, 100); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci case INA209_BUS_VOLTAGE: 12062306a36Sopenharmony_ci case INA209_BUS_VOLTAGE_MAX_PEAK: 12162306a36Sopenharmony_ci case INA209_BUS_VOLTAGE_MIN_PEAK: 12262306a36Sopenharmony_ci case INA209_BUS_VOLTAGE_OVER_WARN: 12362306a36Sopenharmony_ci case INA209_BUS_VOLTAGE_UNDER_WARN: 12462306a36Sopenharmony_ci case INA209_BUS_VOLTAGE_OVER_LIMIT: 12562306a36Sopenharmony_ci case INA209_BUS_VOLTAGE_UNDER_LIMIT: 12662306a36Sopenharmony_ci /* LSB=4 mV, last 3 bits unused */ 12762306a36Sopenharmony_ci return (val >> 3) * 4; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci case INA209_CRITICAL_DAC_POS: 13062306a36Sopenharmony_ci /* LSB=1 mV, in the upper 8 bits */ 13162306a36Sopenharmony_ci return val >> 8; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci case INA209_CRITICAL_DAC_NEG: 13462306a36Sopenharmony_ci /* LSB=1 mV, in the upper 8 bits */ 13562306a36Sopenharmony_ci return -1 * (val >> 8); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci case INA209_POWER: 13862306a36Sopenharmony_ci case INA209_POWER_PEAK: 13962306a36Sopenharmony_ci case INA209_POWER_WARN: 14062306a36Sopenharmony_ci case INA209_POWER_OVER_LIMIT: 14162306a36Sopenharmony_ci /* LSB=20 mW. Convert to uW */ 14262306a36Sopenharmony_ci return val * 20 * 1000L; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci case INA209_CURRENT: 14562306a36Sopenharmony_ci /* LSB=1 mA (selected). Is in mA */ 14662306a36Sopenharmony_ci return (s16)val; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* programmer goofed */ 15062306a36Sopenharmony_ci WARN_ON_ONCE(1); 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/* 15562306a36Sopenharmony_ci * Take a value and convert it to register format, clamping the value 15662306a36Sopenharmony_ci * to the appropriate range. 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_cistatic int ina209_to_reg(u8 reg, u16 old, long val) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci switch (reg) { 16162306a36Sopenharmony_ci case INA209_SHUNT_VOLTAGE_POS_WARN: 16262306a36Sopenharmony_ci case INA209_SHUNT_VOLTAGE_NEG_WARN: 16362306a36Sopenharmony_ci /* Limit to +- 320 mV, 10 uV LSB */ 16462306a36Sopenharmony_ci return clamp_val(val, -320, 320) * 100; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci case INA209_BUS_VOLTAGE_OVER_WARN: 16762306a36Sopenharmony_ci case INA209_BUS_VOLTAGE_UNDER_WARN: 16862306a36Sopenharmony_ci case INA209_BUS_VOLTAGE_OVER_LIMIT: 16962306a36Sopenharmony_ci case INA209_BUS_VOLTAGE_UNDER_LIMIT: 17062306a36Sopenharmony_ci /* 17162306a36Sopenharmony_ci * Limit to 0-32000 mV, 4 mV LSB 17262306a36Sopenharmony_ci * 17362306a36Sopenharmony_ci * The last three bits aren't part of the value, but we'll 17462306a36Sopenharmony_ci * preserve them in their original state. 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_ci return (DIV_ROUND_CLOSEST(clamp_val(val, 0, 32000), 4) << 3) 17762306a36Sopenharmony_ci | (old & 0x7); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci case INA209_CRITICAL_DAC_NEG: 18062306a36Sopenharmony_ci /* 18162306a36Sopenharmony_ci * Limit to -255-0 mV, 1 mV LSB 18262306a36Sopenharmony_ci * Convert the value to a positive value for the register 18362306a36Sopenharmony_ci * 18462306a36Sopenharmony_ci * The value lives in the top 8 bits only, be careful 18562306a36Sopenharmony_ci * and keep original value of other bits. 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ci return (clamp_val(-val, 0, 255) << 8) | (old & 0xff); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci case INA209_CRITICAL_DAC_POS: 19062306a36Sopenharmony_ci /* 19162306a36Sopenharmony_ci * Limit to 0-255 mV, 1 mV LSB 19262306a36Sopenharmony_ci * 19362306a36Sopenharmony_ci * The value lives in the top 8 bits only, be careful 19462306a36Sopenharmony_ci * and keep original value of other bits. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ci return (clamp_val(val, 0, 255) << 8) | (old & 0xff); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci case INA209_POWER_WARN: 19962306a36Sopenharmony_ci case INA209_POWER_OVER_LIMIT: 20062306a36Sopenharmony_ci /* 20 mW LSB */ 20162306a36Sopenharmony_ci return DIV_ROUND_CLOSEST(val, 20 * 1000); 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Other registers are read-only, return access error */ 20562306a36Sopenharmony_ci return -EACCES; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int ina209_interval_from_reg(u16 reg) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci return 68 >> (15 - ((reg >> 3) & 0x0f)); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic u16 ina209_reg_from_interval(u16 config, long interval) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci int i, adc; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (interval <= 0) { 21862306a36Sopenharmony_ci adc = 8; 21962306a36Sopenharmony_ci } else { 22062306a36Sopenharmony_ci adc = 15; 22162306a36Sopenharmony_ci for (i = 34 + 34 / 2; i; i >>= 1) { 22262306a36Sopenharmony_ci if (i < interval) 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci adc--; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci return (config & 0xf807) | (adc << 3) | (adc << 7); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic ssize_t ina209_interval_store(struct device *dev, 23162306a36Sopenharmony_ci struct device_attribute *da, 23262306a36Sopenharmony_ci const char *buf, size_t count) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct ina209_data *data = ina209_update_device(dev); 23562306a36Sopenharmony_ci long val; 23662306a36Sopenharmony_ci u16 regval; 23762306a36Sopenharmony_ci int ret; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (IS_ERR(data)) 24062306a36Sopenharmony_ci return PTR_ERR(data); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci ret = kstrtol(buf, 10, &val); 24362306a36Sopenharmony_ci if (ret < 0) 24462306a36Sopenharmony_ci return ret; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci mutex_lock(&data->update_lock); 24762306a36Sopenharmony_ci regval = ina209_reg_from_interval(data->regs[INA209_CONFIGURATION], 24862306a36Sopenharmony_ci val); 24962306a36Sopenharmony_ci i2c_smbus_write_word_swapped(data->client, INA209_CONFIGURATION, 25062306a36Sopenharmony_ci regval); 25162306a36Sopenharmony_ci data->regs[INA209_CONFIGURATION] = regval; 25262306a36Sopenharmony_ci data->update_interval = ina209_interval_from_reg(regval); 25362306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 25462306a36Sopenharmony_ci return count; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic ssize_t ina209_interval_show(struct device *dev, 25862306a36Sopenharmony_ci struct device_attribute *da, char *buf) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct ina209_data *data = dev_get_drvdata(dev); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", data->update_interval); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/* 26662306a36Sopenharmony_ci * History is reset by writing 1 into bit 0 of the respective peak register. 26762306a36Sopenharmony_ci * Since more than one peak register may be affected by the scope of a 26862306a36Sopenharmony_ci * reset_history attribute write, use a bit mask in attr->index to identify 26962306a36Sopenharmony_ci * which registers are affected. 27062306a36Sopenharmony_ci */ 27162306a36Sopenharmony_cistatic u16 ina209_reset_history_regs[] = { 27262306a36Sopenharmony_ci INA209_SHUNT_VOLTAGE_POS_PEAK, 27362306a36Sopenharmony_ci INA209_SHUNT_VOLTAGE_NEG_PEAK, 27462306a36Sopenharmony_ci INA209_BUS_VOLTAGE_MAX_PEAK, 27562306a36Sopenharmony_ci INA209_BUS_VOLTAGE_MIN_PEAK, 27662306a36Sopenharmony_ci INA209_POWER_PEAK 27762306a36Sopenharmony_ci}; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic ssize_t ina209_history_store(struct device *dev, 28062306a36Sopenharmony_ci struct device_attribute *da, 28162306a36Sopenharmony_ci const char *buf, size_t count) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 28462306a36Sopenharmony_ci struct ina209_data *data = dev_get_drvdata(dev); 28562306a36Sopenharmony_ci struct i2c_client *client = data->client; 28662306a36Sopenharmony_ci u32 mask = attr->index; 28762306a36Sopenharmony_ci long val; 28862306a36Sopenharmony_ci int i, ret; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci ret = kstrtol(buf, 10, &val); 29162306a36Sopenharmony_ci if (ret < 0) 29262306a36Sopenharmony_ci return ret; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci mutex_lock(&data->update_lock); 29562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ina209_reset_history_regs); i++) { 29662306a36Sopenharmony_ci if (mask & (1 << i)) 29762306a36Sopenharmony_ci i2c_smbus_write_word_swapped(client, 29862306a36Sopenharmony_ci ina209_reset_history_regs[i], 1); 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci data->valid = false; 30162306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 30262306a36Sopenharmony_ci return count; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic ssize_t ina209_value_store(struct device *dev, 30662306a36Sopenharmony_ci struct device_attribute *da, 30762306a36Sopenharmony_ci const char *buf, size_t count) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct ina209_data *data = ina209_update_device(dev); 31062306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 31162306a36Sopenharmony_ci int reg = attr->index; 31262306a36Sopenharmony_ci long val; 31362306a36Sopenharmony_ci int ret; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (IS_ERR(data)) 31662306a36Sopenharmony_ci return PTR_ERR(data); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci ret = kstrtol(buf, 10, &val); 31962306a36Sopenharmony_ci if (ret < 0) 32062306a36Sopenharmony_ci return ret; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci mutex_lock(&data->update_lock); 32362306a36Sopenharmony_ci ret = ina209_to_reg(reg, data->regs[reg], val); 32462306a36Sopenharmony_ci if (ret < 0) { 32562306a36Sopenharmony_ci count = ret; 32662306a36Sopenharmony_ci goto abort; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci i2c_smbus_write_word_swapped(data->client, reg, ret); 32962306a36Sopenharmony_ci data->regs[reg] = ret; 33062306a36Sopenharmony_ciabort: 33162306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 33262306a36Sopenharmony_ci return count; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic ssize_t ina209_value_show(struct device *dev, 33662306a36Sopenharmony_ci struct device_attribute *da, char *buf) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 33962306a36Sopenharmony_ci struct ina209_data *data = ina209_update_device(dev); 34062306a36Sopenharmony_ci long val; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (IS_ERR(data)) 34362306a36Sopenharmony_ci return PTR_ERR(data); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci val = ina209_from_reg(attr->index, data->regs[attr->index]); 34662306a36Sopenharmony_ci return sysfs_emit(buf, "%ld\n", val); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic ssize_t ina209_alarm_show(struct device *dev, 35062306a36Sopenharmony_ci struct device_attribute *da, char *buf) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 35362306a36Sopenharmony_ci struct ina209_data *data = ina209_update_device(dev); 35462306a36Sopenharmony_ci const unsigned int mask = attr->index; 35562306a36Sopenharmony_ci u16 status; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (IS_ERR(data)) 35862306a36Sopenharmony_ci return PTR_ERR(data); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci status = data->regs[INA209_STATUS]; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* 36362306a36Sopenharmony_ci * All alarms are in the INA209_STATUS register. To avoid a long 36462306a36Sopenharmony_ci * switch statement, the mask is passed in attr->index 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", !!(status & mask)); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci/* Shunt voltage, history, limits, alarms */ 37062306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in0_input, ina209_value, INA209_SHUNT_VOLTAGE); 37162306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in0_input_highest, ina209_value, 37262306a36Sopenharmony_ci INA209_SHUNT_VOLTAGE_POS_PEAK); 37362306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in0_input_lowest, ina209_value, 37462306a36Sopenharmony_ci INA209_SHUNT_VOLTAGE_NEG_PEAK); 37562306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_WO(in0_reset_history, ina209_history, 37662306a36Sopenharmony_ci (1 << 0) | (1 << 1)); 37762306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in0_max, ina209_value, 37862306a36Sopenharmony_ci INA209_SHUNT_VOLTAGE_POS_WARN); 37962306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in0_min, ina209_value, 38062306a36Sopenharmony_ci INA209_SHUNT_VOLTAGE_NEG_WARN); 38162306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in0_crit_max, ina209_value, 38262306a36Sopenharmony_ci INA209_CRITICAL_DAC_POS); 38362306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in0_crit_min, ina209_value, 38462306a36Sopenharmony_ci INA209_CRITICAL_DAC_NEG); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in0_min_alarm, ina209_alarm, 1 << 11); 38762306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in0_max_alarm, ina209_alarm, 1 << 12); 38862306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in0_crit_min_alarm, ina209_alarm, 1 << 6); 38962306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in0_crit_max_alarm, ina209_alarm, 1 << 7); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci/* Bus voltage, history, limits, alarms */ 39262306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_input, ina209_value, INA209_BUS_VOLTAGE); 39362306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_input_highest, ina209_value, 39462306a36Sopenharmony_ci INA209_BUS_VOLTAGE_MAX_PEAK); 39562306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_input_lowest, ina209_value, 39662306a36Sopenharmony_ci INA209_BUS_VOLTAGE_MIN_PEAK); 39762306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_WO(in1_reset_history, ina209_history, 39862306a36Sopenharmony_ci (1 << 2) | (1 << 3)); 39962306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in1_max, ina209_value, 40062306a36Sopenharmony_ci INA209_BUS_VOLTAGE_OVER_WARN); 40162306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in1_min, ina209_value, 40262306a36Sopenharmony_ci INA209_BUS_VOLTAGE_UNDER_WARN); 40362306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in1_crit_max, ina209_value, 40462306a36Sopenharmony_ci INA209_BUS_VOLTAGE_OVER_LIMIT); 40562306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(in1_crit_min, ina209_value, 40662306a36Sopenharmony_ci INA209_BUS_VOLTAGE_UNDER_LIMIT); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_min_alarm, ina209_alarm, 1 << 14); 40962306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_max_alarm, ina209_alarm, 1 << 15); 41062306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_crit_min_alarm, ina209_alarm, 1 << 9); 41162306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_crit_max_alarm, ina209_alarm, 1 << 10); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci/* Power */ 41462306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(power1_input, ina209_value, INA209_POWER); 41562306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(power1_input_highest, ina209_value, 41662306a36Sopenharmony_ci INA209_POWER_PEAK); 41762306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_WO(power1_reset_history, ina209_history, 1 << 4); 41862306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(power1_max, ina209_value, INA209_POWER_WARN); 41962306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(power1_crit, ina209_value, 42062306a36Sopenharmony_ci INA209_POWER_OVER_LIMIT); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(power1_max_alarm, ina209_alarm, 1 << 13); 42362306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(power1_crit_alarm, ina209_alarm, 1 << 8); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci/* Current */ 42662306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(curr1_input, ina209_value, INA209_CURRENT); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(update_interval, ina209_interval, 0); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci/* 43162306a36Sopenharmony_ci * Finally, construct an array of pointers to members of the above objects, 43262306a36Sopenharmony_ci * as required for sysfs_create_group() 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_cistatic struct attribute *ina209_attrs[] = { 43562306a36Sopenharmony_ci &sensor_dev_attr_in0_input.dev_attr.attr, 43662306a36Sopenharmony_ci &sensor_dev_attr_in0_input_highest.dev_attr.attr, 43762306a36Sopenharmony_ci &sensor_dev_attr_in0_input_lowest.dev_attr.attr, 43862306a36Sopenharmony_ci &sensor_dev_attr_in0_reset_history.dev_attr.attr, 43962306a36Sopenharmony_ci &sensor_dev_attr_in0_max.dev_attr.attr, 44062306a36Sopenharmony_ci &sensor_dev_attr_in0_min.dev_attr.attr, 44162306a36Sopenharmony_ci &sensor_dev_attr_in0_crit_max.dev_attr.attr, 44262306a36Sopenharmony_ci &sensor_dev_attr_in0_crit_min.dev_attr.attr, 44362306a36Sopenharmony_ci &sensor_dev_attr_in0_max_alarm.dev_attr.attr, 44462306a36Sopenharmony_ci &sensor_dev_attr_in0_min_alarm.dev_attr.attr, 44562306a36Sopenharmony_ci &sensor_dev_attr_in0_crit_max_alarm.dev_attr.attr, 44662306a36Sopenharmony_ci &sensor_dev_attr_in0_crit_min_alarm.dev_attr.attr, 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci &sensor_dev_attr_in1_input.dev_attr.attr, 44962306a36Sopenharmony_ci &sensor_dev_attr_in1_input_highest.dev_attr.attr, 45062306a36Sopenharmony_ci &sensor_dev_attr_in1_input_lowest.dev_attr.attr, 45162306a36Sopenharmony_ci &sensor_dev_attr_in1_reset_history.dev_attr.attr, 45262306a36Sopenharmony_ci &sensor_dev_attr_in1_max.dev_attr.attr, 45362306a36Sopenharmony_ci &sensor_dev_attr_in1_min.dev_attr.attr, 45462306a36Sopenharmony_ci &sensor_dev_attr_in1_crit_max.dev_attr.attr, 45562306a36Sopenharmony_ci &sensor_dev_attr_in1_crit_min.dev_attr.attr, 45662306a36Sopenharmony_ci &sensor_dev_attr_in1_max_alarm.dev_attr.attr, 45762306a36Sopenharmony_ci &sensor_dev_attr_in1_min_alarm.dev_attr.attr, 45862306a36Sopenharmony_ci &sensor_dev_attr_in1_crit_max_alarm.dev_attr.attr, 45962306a36Sopenharmony_ci &sensor_dev_attr_in1_crit_min_alarm.dev_attr.attr, 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci &sensor_dev_attr_power1_input.dev_attr.attr, 46262306a36Sopenharmony_ci &sensor_dev_attr_power1_input_highest.dev_attr.attr, 46362306a36Sopenharmony_ci &sensor_dev_attr_power1_reset_history.dev_attr.attr, 46462306a36Sopenharmony_ci &sensor_dev_attr_power1_max.dev_attr.attr, 46562306a36Sopenharmony_ci &sensor_dev_attr_power1_crit.dev_attr.attr, 46662306a36Sopenharmony_ci &sensor_dev_attr_power1_max_alarm.dev_attr.attr, 46762306a36Sopenharmony_ci &sensor_dev_attr_power1_crit_alarm.dev_attr.attr, 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci &sensor_dev_attr_curr1_input.dev_attr.attr, 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci &sensor_dev_attr_update_interval.dev_attr.attr, 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci NULL, 47462306a36Sopenharmony_ci}; 47562306a36Sopenharmony_ciATTRIBUTE_GROUPS(ina209); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic void ina209_restore_conf(struct i2c_client *client, 47862306a36Sopenharmony_ci struct ina209_data *data) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci /* Restore initial configuration */ 48162306a36Sopenharmony_ci i2c_smbus_write_word_swapped(client, INA209_CONFIGURATION, 48262306a36Sopenharmony_ci data->config_orig); 48362306a36Sopenharmony_ci i2c_smbus_write_word_swapped(client, INA209_CALIBRATION, 48462306a36Sopenharmony_ci data->calibration_orig); 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic int ina209_init_client(struct i2c_client *client, 48862306a36Sopenharmony_ci struct ina209_data *data) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct ina2xx_platform_data *pdata = dev_get_platdata(&client->dev); 49162306a36Sopenharmony_ci u32 shunt; 49262306a36Sopenharmony_ci int reg; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci reg = i2c_smbus_read_word_swapped(client, INA209_CALIBRATION); 49562306a36Sopenharmony_ci if (reg < 0) 49662306a36Sopenharmony_ci return reg; 49762306a36Sopenharmony_ci data->calibration_orig = reg; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci reg = i2c_smbus_read_word_swapped(client, INA209_CONFIGURATION); 50062306a36Sopenharmony_ci if (reg < 0) 50162306a36Sopenharmony_ci return reg; 50262306a36Sopenharmony_ci data->config_orig = reg; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (pdata) { 50562306a36Sopenharmony_ci if (pdata->shunt_uohms <= 0) 50662306a36Sopenharmony_ci return -EINVAL; 50762306a36Sopenharmony_ci shunt = pdata->shunt_uohms; 50862306a36Sopenharmony_ci } else if (!of_property_read_u32(client->dev.of_node, "shunt-resistor", 50962306a36Sopenharmony_ci &shunt)) { 51062306a36Sopenharmony_ci if (shunt == 0) 51162306a36Sopenharmony_ci return -EINVAL; 51262306a36Sopenharmony_ci } else { 51362306a36Sopenharmony_ci shunt = data->calibration_orig ? 51462306a36Sopenharmony_ci 40960000 / data->calibration_orig : INA209_SHUNT_DEFAULT; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci i2c_smbus_write_word_swapped(client, INA209_CONFIGURATION, 51862306a36Sopenharmony_ci INA209_CONFIG_DEFAULT); 51962306a36Sopenharmony_ci data->update_interval = ina209_interval_from_reg(INA209_CONFIG_DEFAULT); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* 52262306a36Sopenharmony_ci * Calibrate current LSB to 1mA. Shunt is in uOhms. 52362306a36Sopenharmony_ci * See equation 13 in datasheet. 52462306a36Sopenharmony_ci */ 52562306a36Sopenharmony_ci i2c_smbus_write_word_swapped(client, INA209_CALIBRATION, 52662306a36Sopenharmony_ci clamp_val(40960000 / shunt, 1, 65535)); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* Clear status register */ 52962306a36Sopenharmony_ci i2c_smbus_read_word_swapped(client, INA209_STATUS); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci return 0; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic int ina209_probe(struct i2c_client *client) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 53762306a36Sopenharmony_ci struct ina209_data *data; 53862306a36Sopenharmony_ci struct device *hwmon_dev; 53962306a36Sopenharmony_ci int ret; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) 54262306a36Sopenharmony_ci return -ENODEV; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); 54562306a36Sopenharmony_ci if (!data) 54662306a36Sopenharmony_ci return -ENOMEM; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci i2c_set_clientdata(client, data); 54962306a36Sopenharmony_ci data->client = client; 55062306a36Sopenharmony_ci mutex_init(&data->update_lock); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci ret = ina209_init_client(client, data); 55362306a36Sopenharmony_ci if (ret) 55462306a36Sopenharmony_ci return ret; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, 55762306a36Sopenharmony_ci client->name, 55862306a36Sopenharmony_ci data, ina209_groups); 55962306a36Sopenharmony_ci if (IS_ERR(hwmon_dev)) { 56062306a36Sopenharmony_ci ret = PTR_ERR(hwmon_dev); 56162306a36Sopenharmony_ci goto out_restore_conf; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci return 0; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ciout_restore_conf: 56762306a36Sopenharmony_ci ina209_restore_conf(client, data); 56862306a36Sopenharmony_ci return ret; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic void ina209_remove(struct i2c_client *client) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct ina209_data *data = i2c_get_clientdata(client); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci ina209_restore_conf(client, data); 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic const struct i2c_device_id ina209_id[] = { 57962306a36Sopenharmony_ci { "ina209", 0 }, 58062306a36Sopenharmony_ci { } 58162306a36Sopenharmony_ci}; 58262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ina209_id); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic const struct of_device_id __maybe_unused ina209_of_match[] = { 58562306a36Sopenharmony_ci { .compatible = "ti,ina209" }, 58662306a36Sopenharmony_ci { }, 58762306a36Sopenharmony_ci}; 58862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ina209_of_match); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci/* This is the driver that will be inserted */ 59162306a36Sopenharmony_cistatic struct i2c_driver ina209_driver = { 59262306a36Sopenharmony_ci .class = I2C_CLASS_HWMON, 59362306a36Sopenharmony_ci .driver = { 59462306a36Sopenharmony_ci .name = "ina209", 59562306a36Sopenharmony_ci .of_match_table = of_match_ptr(ina209_of_match), 59662306a36Sopenharmony_ci }, 59762306a36Sopenharmony_ci .probe = ina209_probe, 59862306a36Sopenharmony_ci .remove = ina209_remove, 59962306a36Sopenharmony_ci .id_table = ina209_id, 60062306a36Sopenharmony_ci}; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cimodule_i2c_driver(ina209_driver); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ciMODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>, Paul Hays <Paul.Hays@cattail.ca>, Guenter Roeck <linux@roeck-us.net>"); 60562306a36Sopenharmony_ciMODULE_DESCRIPTION("INA209 driver"); 60662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 607