17cb6dcffSAndrew F. Davis /* 27cb6dcffSAndrew F. Davis * INA3221 Triple Current/Voltage Monitor 37cb6dcffSAndrew F. Davis * 47cb6dcffSAndrew F. Davis * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/ 57cb6dcffSAndrew F. Davis * Andrew F. Davis <afd@ti.com> 67cb6dcffSAndrew F. Davis * 77cb6dcffSAndrew F. Davis * This program is free software; you can redistribute it and/or modify 87cb6dcffSAndrew F. Davis * it under the terms of the GNU General Public License version 2 as 97cb6dcffSAndrew F. Davis * published by the Free Software Foundation. 107cb6dcffSAndrew F. Davis * 117cb6dcffSAndrew F. Davis * This program is distributed in the hope that it will be useful, but 127cb6dcffSAndrew F. Davis * WITHOUT ANY WARRANTY; without even the implied warranty of 137cb6dcffSAndrew F. Davis * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 147cb6dcffSAndrew F. Davis * General Public License for more details. 157cb6dcffSAndrew F. Davis */ 167cb6dcffSAndrew F. Davis 177cb6dcffSAndrew F. Davis #include <linux/hwmon.h> 187cb6dcffSAndrew F. Davis #include <linux/hwmon-sysfs.h> 197cb6dcffSAndrew F. Davis #include <linux/i2c.h> 207cb6dcffSAndrew F. Davis #include <linux/module.h> 217cb6dcffSAndrew F. Davis #include <linux/of.h> 227cb6dcffSAndrew F. Davis #include <linux/regmap.h> 237cb6dcffSAndrew F. Davis 247cb6dcffSAndrew F. Davis #define INA3221_DRIVER_NAME "ina3221" 257cb6dcffSAndrew F. Davis 267cb6dcffSAndrew F. Davis #define INA3221_CONFIG 0x00 277cb6dcffSAndrew F. Davis #define INA3221_SHUNT1 0x01 287cb6dcffSAndrew F. Davis #define INA3221_BUS1 0x02 297cb6dcffSAndrew F. Davis #define INA3221_SHUNT2 0x03 307cb6dcffSAndrew F. Davis #define INA3221_BUS2 0x04 317cb6dcffSAndrew F. Davis #define INA3221_SHUNT3 0x05 327cb6dcffSAndrew F. Davis #define INA3221_BUS3 0x06 337cb6dcffSAndrew F. Davis #define INA3221_CRIT1 0x07 347cb6dcffSAndrew F. Davis #define INA3221_WARN1 0x08 357cb6dcffSAndrew F. Davis #define INA3221_CRIT2 0x09 367cb6dcffSAndrew F. Davis #define INA3221_WARN2 0x0a 377cb6dcffSAndrew F. Davis #define INA3221_CRIT3 0x0b 387cb6dcffSAndrew F. Davis #define INA3221_WARN3 0x0c 397cb6dcffSAndrew F. Davis #define INA3221_MASK_ENABLE 0x0f 407cb6dcffSAndrew F. Davis 4159d608e1SNicolin Chen #define INA3221_CONFIG_MODE_MASK GENMASK(2, 0) 4259d608e1SNicolin Chen #define INA3221_CONFIG_MODE_POWERDOWN 0 43791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_SHUNT BIT(0) 44791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_BUS BIT(1) 45791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_CONTINUOUS BIT(2) 46a9e9dd9cSNicolin Chen #define INA3221_CONFIG_CHx_EN(x) BIT(14 - (x)) 477cb6dcffSAndrew F. Davis 487cb6dcffSAndrew F. Davis #define INA3221_RSHUNT_DEFAULT 10000 497cb6dcffSAndrew F. Davis 507cb6dcffSAndrew F. Davis enum ina3221_fields { 517cb6dcffSAndrew F. Davis /* Configuration */ 527cb6dcffSAndrew F. Davis F_RST, 537cb6dcffSAndrew F. Davis 547cb6dcffSAndrew F. Davis /* Alert Flags */ 557cb6dcffSAndrew F. Davis F_WF3, F_WF2, F_WF1, 567cb6dcffSAndrew F. Davis F_CF3, F_CF2, F_CF1, 577cb6dcffSAndrew F. Davis 587cb6dcffSAndrew F. Davis /* sentinel */ 597cb6dcffSAndrew F. Davis F_MAX_FIELDS 607cb6dcffSAndrew F. Davis }; 617cb6dcffSAndrew F. Davis 627cb6dcffSAndrew F. Davis static const struct reg_field ina3221_reg_fields[] = { 637cb6dcffSAndrew F. Davis [F_RST] = REG_FIELD(INA3221_CONFIG, 15, 15), 647cb6dcffSAndrew F. Davis 657cb6dcffSAndrew F. Davis [F_WF3] = REG_FIELD(INA3221_MASK_ENABLE, 3, 3), 667cb6dcffSAndrew F. Davis [F_WF2] = REG_FIELD(INA3221_MASK_ENABLE, 4, 4), 677cb6dcffSAndrew F. Davis [F_WF1] = REG_FIELD(INA3221_MASK_ENABLE, 5, 5), 687cb6dcffSAndrew F. Davis [F_CF3] = REG_FIELD(INA3221_MASK_ENABLE, 7, 7), 697cb6dcffSAndrew F. Davis [F_CF2] = REG_FIELD(INA3221_MASK_ENABLE, 8, 8), 707cb6dcffSAndrew F. Davis [F_CF1] = REG_FIELD(INA3221_MASK_ENABLE, 9, 9), 717cb6dcffSAndrew F. Davis }; 727cb6dcffSAndrew F. Davis 737cb6dcffSAndrew F. Davis enum ina3221_channels { 747cb6dcffSAndrew F. Davis INA3221_CHANNEL1, 757cb6dcffSAndrew F. Davis INA3221_CHANNEL2, 767cb6dcffSAndrew F. Davis INA3221_CHANNEL3, 777cb6dcffSAndrew F. Davis INA3221_NUM_CHANNELS 787cb6dcffSAndrew F. Davis }; 797cb6dcffSAndrew F. Davis 807cb6dcffSAndrew F. Davis static const unsigned int register_channel[] = { 81a9e9dd9cSNicolin Chen [INA3221_BUS1] = INA3221_CHANNEL1, 82a9e9dd9cSNicolin Chen [INA3221_BUS2] = INA3221_CHANNEL2, 83a9e9dd9cSNicolin Chen [INA3221_BUS3] = INA3221_CHANNEL3, 847cb6dcffSAndrew F. Davis [INA3221_SHUNT1] = INA3221_CHANNEL1, 857cb6dcffSAndrew F. Davis [INA3221_SHUNT2] = INA3221_CHANNEL2, 867cb6dcffSAndrew F. Davis [INA3221_SHUNT3] = INA3221_CHANNEL3, 877cb6dcffSAndrew F. Davis [INA3221_CRIT1] = INA3221_CHANNEL1, 887cb6dcffSAndrew F. Davis [INA3221_CRIT2] = INA3221_CHANNEL2, 897cb6dcffSAndrew F. Davis [INA3221_CRIT3] = INA3221_CHANNEL3, 907cb6dcffSAndrew F. Davis [INA3221_WARN1] = INA3221_CHANNEL1, 917cb6dcffSAndrew F. Davis [INA3221_WARN2] = INA3221_CHANNEL2, 927cb6dcffSAndrew F. Davis [INA3221_WARN3] = INA3221_CHANNEL3, 937cb6dcffSAndrew F. Davis }; 947cb6dcffSAndrew F. Davis 957cb6dcffSAndrew F. Davis /** 96a9e9dd9cSNicolin Chen * struct ina3221_input - channel input source specific information 97a9e9dd9cSNicolin Chen * @label: label of channel input source 98a9e9dd9cSNicolin Chen * @shunt_resistor: shunt resistor value of channel input source 99a9e9dd9cSNicolin Chen * @disconnected: connection status of channel input source 100a9e9dd9cSNicolin Chen */ 101a9e9dd9cSNicolin Chen struct ina3221_input { 102a9e9dd9cSNicolin Chen const char *label; 103a9e9dd9cSNicolin Chen int shunt_resistor; 104a9e9dd9cSNicolin Chen bool disconnected; 105a9e9dd9cSNicolin Chen }; 106a9e9dd9cSNicolin Chen 107a9e9dd9cSNicolin Chen /** 1087cb6dcffSAndrew F. Davis * struct ina3221_data - device specific information 1097cb6dcffSAndrew F. Davis * @regmap: Register map of the device 1107cb6dcffSAndrew F. Davis * @fields: Register fields of the device 111a9e9dd9cSNicolin Chen * @inputs: Array of channel input source specific structures 11259d608e1SNicolin Chen * @reg_config: Register value of INA3221_CONFIG 1137cb6dcffSAndrew F. Davis */ 1147cb6dcffSAndrew F. Davis struct ina3221_data { 1157cb6dcffSAndrew F. Davis struct regmap *regmap; 1167cb6dcffSAndrew F. Davis struct regmap_field *fields[F_MAX_FIELDS]; 117a9e9dd9cSNicolin Chen struct ina3221_input inputs[INA3221_NUM_CHANNELS]; 11859d608e1SNicolin Chen u32 reg_config; 1197cb6dcffSAndrew F. Davis }; 1207cb6dcffSAndrew F. Davis 121a9e9dd9cSNicolin Chen static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel) 122a9e9dd9cSNicolin Chen { 123a9e9dd9cSNicolin Chen return ina->reg_config & INA3221_CONFIG_CHx_EN(channel); 124a9e9dd9cSNicolin Chen } 125a9e9dd9cSNicolin Chen 126a9e9dd9cSNicolin Chen static ssize_t ina3221_show_label(struct device *dev, 127a9e9dd9cSNicolin Chen struct device_attribute *attr, char *buf) 128a9e9dd9cSNicolin Chen { 129a9e9dd9cSNicolin Chen struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); 130a9e9dd9cSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 131a9e9dd9cSNicolin Chen unsigned int channel = sd_attr->index; 132a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 133a9e9dd9cSNicolin Chen 134a9e9dd9cSNicolin Chen return snprintf(buf, PAGE_SIZE, "%s\n", input->label); 135a9e9dd9cSNicolin Chen } 136a9e9dd9cSNicolin Chen 137a9e9dd9cSNicolin Chen static ssize_t ina3221_show_enable(struct device *dev, 138a9e9dd9cSNicolin Chen struct device_attribute *attr, char *buf) 139a9e9dd9cSNicolin Chen { 140a9e9dd9cSNicolin Chen struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); 141a9e9dd9cSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 142a9e9dd9cSNicolin Chen unsigned int channel = sd_attr->index; 143a9e9dd9cSNicolin Chen 144a9e9dd9cSNicolin Chen return snprintf(buf, PAGE_SIZE, "%d\n", 145a9e9dd9cSNicolin Chen ina3221_is_enabled(ina, channel)); 146a9e9dd9cSNicolin Chen } 147a9e9dd9cSNicolin Chen 148a9e9dd9cSNicolin Chen static ssize_t ina3221_set_enable(struct device *dev, 149a9e9dd9cSNicolin Chen struct device_attribute *attr, 150a9e9dd9cSNicolin Chen const char *buf, size_t count) 151a9e9dd9cSNicolin Chen { 152a9e9dd9cSNicolin Chen struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); 153a9e9dd9cSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 154a9e9dd9cSNicolin Chen unsigned int channel = sd_attr->index; 155a9e9dd9cSNicolin Chen u16 config, mask = INA3221_CONFIG_CHx_EN(channel); 156a9e9dd9cSNicolin Chen bool enable; 157a9e9dd9cSNicolin Chen int ret; 158a9e9dd9cSNicolin Chen 159a9e9dd9cSNicolin Chen ret = kstrtobool(buf, &enable); 160a9e9dd9cSNicolin Chen if (ret) 161a9e9dd9cSNicolin Chen return ret; 162a9e9dd9cSNicolin Chen 163a9e9dd9cSNicolin Chen config = enable ? mask : 0; 164a9e9dd9cSNicolin Chen 165a9e9dd9cSNicolin Chen /* Enable or disable the channel */ 166a9e9dd9cSNicolin Chen ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, mask, config); 167a9e9dd9cSNicolin Chen if (ret) 168a9e9dd9cSNicolin Chen return ret; 169a9e9dd9cSNicolin Chen 170a9e9dd9cSNicolin Chen /* Cache the latest config register value */ 171a9e9dd9cSNicolin Chen ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config); 172a9e9dd9cSNicolin Chen if (ret) 173a9e9dd9cSNicolin Chen return ret; 174a9e9dd9cSNicolin Chen 175a9e9dd9cSNicolin Chen return count; 176a9e9dd9cSNicolin Chen } 177a9e9dd9cSNicolin Chen 1787cb6dcffSAndrew F. Davis static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg, 1797cb6dcffSAndrew F. Davis int *val) 1807cb6dcffSAndrew F. Davis { 1817cb6dcffSAndrew F. Davis unsigned int regval; 1827cb6dcffSAndrew F. Davis int ret; 1837cb6dcffSAndrew F. Davis 1847cb6dcffSAndrew F. Davis ret = regmap_read(ina->regmap, reg, ®val); 1857cb6dcffSAndrew F. Davis if (ret) 1867cb6dcffSAndrew F. Davis return ret; 1877cb6dcffSAndrew F. Davis 1887cb6dcffSAndrew F. Davis *val = sign_extend32(regval >> 3, 12); 1897cb6dcffSAndrew F. Davis 1907cb6dcffSAndrew F. Davis return 0; 1917cb6dcffSAndrew F. Davis } 1927cb6dcffSAndrew F. Davis 1937cb6dcffSAndrew F. Davis static ssize_t ina3221_show_bus_voltage(struct device *dev, 1947cb6dcffSAndrew F. Davis struct device_attribute *attr, 1957cb6dcffSAndrew F. Davis char *buf) 1967cb6dcffSAndrew F. Davis { 1977cb6dcffSAndrew F. Davis struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); 1987cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 1997cb6dcffSAndrew F. Davis unsigned int reg = sd_attr->index; 200a9e9dd9cSNicolin Chen unsigned int channel = register_channel[reg]; 2017cb6dcffSAndrew F. Davis int val, voltage_mv, ret; 2027cb6dcffSAndrew F. Davis 203a9e9dd9cSNicolin Chen /* No data for read-only attribute if channel is disabled */ 204a9e9dd9cSNicolin Chen if (!attr->store && !ina3221_is_enabled(ina, channel)) 205a9e9dd9cSNicolin Chen return -ENODATA; 206a9e9dd9cSNicolin Chen 2077cb6dcffSAndrew F. Davis ret = ina3221_read_value(ina, reg, &val); 2087cb6dcffSAndrew F. Davis if (ret) 2097cb6dcffSAndrew F. Davis return ret; 2107cb6dcffSAndrew F. Davis 2117cb6dcffSAndrew F. Davis voltage_mv = val * 8; 2127cb6dcffSAndrew F. Davis 2137cb6dcffSAndrew F. Davis return snprintf(buf, PAGE_SIZE, "%d\n", voltage_mv); 2147cb6dcffSAndrew F. Davis } 2157cb6dcffSAndrew F. Davis 2167cb6dcffSAndrew F. Davis static ssize_t ina3221_show_shunt_voltage(struct device *dev, 2177cb6dcffSAndrew F. Davis struct device_attribute *attr, 2187cb6dcffSAndrew F. Davis char *buf) 2197cb6dcffSAndrew F. Davis { 2207cb6dcffSAndrew F. Davis struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); 2217cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 2227cb6dcffSAndrew F. Davis unsigned int reg = sd_attr->index; 223a9e9dd9cSNicolin Chen unsigned int channel = register_channel[reg]; 2247cb6dcffSAndrew F. Davis int val, voltage_uv, ret; 2257cb6dcffSAndrew F. Davis 226a9e9dd9cSNicolin Chen /* No data for read-only attribute if channel is disabled */ 227a9e9dd9cSNicolin Chen if (!attr->store && !ina3221_is_enabled(ina, channel)) 228a9e9dd9cSNicolin Chen return -ENODATA; 229a9e9dd9cSNicolin Chen 2307cb6dcffSAndrew F. Davis ret = ina3221_read_value(ina, reg, &val); 2317cb6dcffSAndrew F. Davis if (ret) 2327cb6dcffSAndrew F. Davis return ret; 2337cb6dcffSAndrew F. Davis voltage_uv = val * 40; 2347cb6dcffSAndrew F. Davis 2357cb6dcffSAndrew F. Davis return snprintf(buf, PAGE_SIZE, "%d\n", voltage_uv); 2367cb6dcffSAndrew F. Davis } 2377cb6dcffSAndrew F. Davis 2387cb6dcffSAndrew F. Davis static ssize_t ina3221_show_current(struct device *dev, 2397cb6dcffSAndrew F. Davis struct device_attribute *attr, char *buf) 2407cb6dcffSAndrew F. Davis { 2417cb6dcffSAndrew F. Davis struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); 2427cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 2437cb6dcffSAndrew F. Davis unsigned int reg = sd_attr->index; 2447cb6dcffSAndrew F. Davis unsigned int channel = register_channel[reg]; 245a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 246a9e9dd9cSNicolin Chen int resistance_uo = input->shunt_resistor; 2477cb6dcffSAndrew F. Davis int val, current_ma, voltage_nv, ret; 2487cb6dcffSAndrew F. Davis 249a9e9dd9cSNicolin Chen /* No data for read-only attribute if channel is disabled */ 250a9e9dd9cSNicolin Chen if (!attr->store && !ina3221_is_enabled(ina, channel)) 251a9e9dd9cSNicolin Chen return -ENODATA; 252a9e9dd9cSNicolin Chen 2537cb6dcffSAndrew F. Davis ret = ina3221_read_value(ina, reg, &val); 2547cb6dcffSAndrew F. Davis if (ret) 2557cb6dcffSAndrew F. Davis return ret; 2567cb6dcffSAndrew F. Davis voltage_nv = val * 40000; 2577cb6dcffSAndrew F. Davis 2587cb6dcffSAndrew F. Davis current_ma = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo); 2597cb6dcffSAndrew F. Davis 2607cb6dcffSAndrew F. Davis return snprintf(buf, PAGE_SIZE, "%d\n", current_ma); 2617cb6dcffSAndrew F. Davis } 2627cb6dcffSAndrew F. Davis 2637cb6dcffSAndrew F. Davis static ssize_t ina3221_set_current(struct device *dev, 2647cb6dcffSAndrew F. Davis struct device_attribute *attr, 2657cb6dcffSAndrew F. Davis const char *buf, size_t count) 2667cb6dcffSAndrew F. Davis { 2677cb6dcffSAndrew F. Davis struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); 2687cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 2697cb6dcffSAndrew F. Davis unsigned int reg = sd_attr->index; 2707cb6dcffSAndrew F. Davis unsigned int channel = register_channel[reg]; 271a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 272a9e9dd9cSNicolin Chen int resistance_uo = input->shunt_resistor; 2737cb6dcffSAndrew F. Davis int val, current_ma, voltage_uv, ret; 2747cb6dcffSAndrew F. Davis 2757cb6dcffSAndrew F. Davis ret = kstrtoint(buf, 0, ¤t_ma); 2767cb6dcffSAndrew F. Davis if (ret) 2777cb6dcffSAndrew F. Davis return ret; 2787cb6dcffSAndrew F. Davis 2797cb6dcffSAndrew F. Davis /* clamp current */ 2807cb6dcffSAndrew F. Davis current_ma = clamp_val(current_ma, 2817cb6dcffSAndrew F. Davis INT_MIN / resistance_uo, 2827cb6dcffSAndrew F. Davis INT_MAX / resistance_uo); 2837cb6dcffSAndrew F. Davis 2847cb6dcffSAndrew F. Davis voltage_uv = DIV_ROUND_CLOSEST(current_ma * resistance_uo, 1000); 2857cb6dcffSAndrew F. Davis 2867cb6dcffSAndrew F. Davis /* clamp voltage */ 2877cb6dcffSAndrew F. Davis voltage_uv = clamp_val(voltage_uv, -163800, 163800); 2887cb6dcffSAndrew F. Davis 2897cb6dcffSAndrew F. Davis /* 1 / 40uV(scale) << 3(register shift) = 5 */ 2907cb6dcffSAndrew F. Davis val = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8; 2917cb6dcffSAndrew F. Davis 2927cb6dcffSAndrew F. Davis ret = regmap_write(ina->regmap, reg, val); 2937cb6dcffSAndrew F. Davis if (ret) 2947cb6dcffSAndrew F. Davis return ret; 2957cb6dcffSAndrew F. Davis 2967cb6dcffSAndrew F. Davis return count; 2977cb6dcffSAndrew F. Davis } 2987cb6dcffSAndrew F. Davis 2997cb6dcffSAndrew F. Davis static ssize_t ina3221_show_shunt(struct device *dev, 3007cb6dcffSAndrew F. Davis struct device_attribute *attr, char *buf) 3017cb6dcffSAndrew F. Davis { 3027cb6dcffSAndrew F. Davis struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); 3037cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 3047cb6dcffSAndrew F. Davis unsigned int channel = sd_attr->index; 305a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 3067cb6dcffSAndrew F. Davis 307a9e9dd9cSNicolin Chen return snprintf(buf, PAGE_SIZE, "%d\n", input->shunt_resistor); 3087cb6dcffSAndrew F. Davis } 3097cb6dcffSAndrew F. Davis 3107cb6dcffSAndrew F. Davis static ssize_t ina3221_set_shunt(struct device *dev, 3117cb6dcffSAndrew F. Davis struct device_attribute *attr, 3127cb6dcffSAndrew F. Davis const char *buf, size_t count) 3137cb6dcffSAndrew F. Davis { 3147cb6dcffSAndrew F. Davis struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); 3157cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 3167cb6dcffSAndrew F. Davis unsigned int channel = sd_attr->index; 317a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 3189ad0df1aSGuenter Roeck int val; 3197cb6dcffSAndrew F. Davis int ret; 3207cb6dcffSAndrew F. Davis 3219ad0df1aSGuenter Roeck ret = kstrtoint(buf, 0, &val); 3227cb6dcffSAndrew F. Davis if (ret) 3237cb6dcffSAndrew F. Davis return ret; 3247cb6dcffSAndrew F. Davis 3259ad0df1aSGuenter Roeck val = clamp_val(val, 1, INT_MAX); 3267cb6dcffSAndrew F. Davis 327a9e9dd9cSNicolin Chen input->shunt_resistor = val; 3287cb6dcffSAndrew F. Davis 3297cb6dcffSAndrew F. Davis return count; 3307cb6dcffSAndrew F. Davis } 3317cb6dcffSAndrew F. Davis 3327cb6dcffSAndrew F. Davis static ssize_t ina3221_show_alert(struct device *dev, 3337cb6dcffSAndrew F. Davis struct device_attribute *attr, char *buf) 3347cb6dcffSAndrew F. Davis { 3357cb6dcffSAndrew F. Davis struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); 3367cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 3377cb6dcffSAndrew F. Davis unsigned int field = sd_attr->index; 3387cb6dcffSAndrew F. Davis unsigned int regval; 3397cb6dcffSAndrew F. Davis int ret; 3407cb6dcffSAndrew F. Davis 3417cb6dcffSAndrew F. Davis ret = regmap_field_read(ina->fields[field], ®val); 3427cb6dcffSAndrew F. Davis if (ret) 3437cb6dcffSAndrew F. Davis return ret; 3447cb6dcffSAndrew F. Davis 3457cb6dcffSAndrew F. Davis return snprintf(buf, PAGE_SIZE, "%d\n", regval); 3467cb6dcffSAndrew F. Davis } 3477cb6dcffSAndrew F. Davis 348a9e9dd9cSNicolin Chen /* input channel label */ 349a9e9dd9cSNicolin Chen static SENSOR_DEVICE_ATTR(in1_label, 0444, 350a9e9dd9cSNicolin Chen ina3221_show_label, NULL, INA3221_CHANNEL1); 351a9e9dd9cSNicolin Chen static SENSOR_DEVICE_ATTR(in2_label, 0444, 352a9e9dd9cSNicolin Chen ina3221_show_label, NULL, INA3221_CHANNEL2); 353a9e9dd9cSNicolin Chen static SENSOR_DEVICE_ATTR(in3_label, 0444, 354a9e9dd9cSNicolin Chen ina3221_show_label, NULL, INA3221_CHANNEL3); 355a9e9dd9cSNicolin Chen 356a9e9dd9cSNicolin Chen /* voltage channel enable */ 357a9e9dd9cSNicolin Chen static SENSOR_DEVICE_ATTR(in1_enable, 0644, 358a9e9dd9cSNicolin Chen ina3221_show_enable, ina3221_set_enable, INA3221_CHANNEL1); 359a9e9dd9cSNicolin Chen static SENSOR_DEVICE_ATTR(in2_enable, 0644, 360a9e9dd9cSNicolin Chen ina3221_show_enable, ina3221_set_enable, INA3221_CHANNEL2); 361a9e9dd9cSNicolin Chen static SENSOR_DEVICE_ATTR(in3_enable, 0644, 362a9e9dd9cSNicolin Chen ina3221_show_enable, ina3221_set_enable, INA3221_CHANNEL3); 363a9e9dd9cSNicolin Chen 3647cb6dcffSAndrew F. Davis /* bus voltage */ 3657cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, 3667cb6dcffSAndrew F. Davis ina3221_show_bus_voltage, NULL, INA3221_BUS1); 3677cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, 3687cb6dcffSAndrew F. Davis ina3221_show_bus_voltage, NULL, INA3221_BUS2); 3697cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, 3707cb6dcffSAndrew F. Davis ina3221_show_bus_voltage, NULL, INA3221_BUS3); 3717cb6dcffSAndrew F. Davis 3727cb6dcffSAndrew F. Davis /* calculated current */ 3737cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, 3747cb6dcffSAndrew F. Davis ina3221_show_current, NULL, INA3221_SHUNT1); 3757cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, 3767cb6dcffSAndrew F. Davis ina3221_show_current, NULL, INA3221_SHUNT2); 3777cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr3_input, S_IRUGO, 3787cb6dcffSAndrew F. Davis ina3221_show_current, NULL, INA3221_SHUNT3); 3797cb6dcffSAndrew F. Davis 3807cb6dcffSAndrew F. Davis /* shunt resistance */ 3817cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(shunt1_resistor, S_IRUGO | S_IWUSR, 3827cb6dcffSAndrew F. Davis ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL1); 3837cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(shunt2_resistor, S_IRUGO | S_IWUSR, 3847cb6dcffSAndrew F. Davis ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL2); 3857cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(shunt3_resistor, S_IRUGO | S_IWUSR, 3867cb6dcffSAndrew F. Davis ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL3); 3877cb6dcffSAndrew F. Davis 3887cb6dcffSAndrew F. Davis /* critical current */ 3897cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr1_crit, S_IRUGO | S_IWUSR, 3907cb6dcffSAndrew F. Davis ina3221_show_current, ina3221_set_current, INA3221_CRIT1); 3917cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr2_crit, S_IRUGO | S_IWUSR, 3927cb6dcffSAndrew F. Davis ina3221_show_current, ina3221_set_current, INA3221_CRIT2); 3937cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr3_crit, S_IRUGO | S_IWUSR, 3947cb6dcffSAndrew F. Davis ina3221_show_current, ina3221_set_current, INA3221_CRIT3); 3957cb6dcffSAndrew F. Davis 3967cb6dcffSAndrew F. Davis /* critical current alert */ 3977cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr1_crit_alarm, S_IRUGO, 3987cb6dcffSAndrew F. Davis ina3221_show_alert, NULL, F_CF1); 3997cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr2_crit_alarm, S_IRUGO, 4007cb6dcffSAndrew F. Davis ina3221_show_alert, NULL, F_CF2); 4017cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr3_crit_alarm, S_IRUGO, 4027cb6dcffSAndrew F. Davis ina3221_show_alert, NULL, F_CF3); 4037cb6dcffSAndrew F. Davis 4047cb6dcffSAndrew F. Davis /* warning current */ 4057cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr1_max, S_IRUGO | S_IWUSR, 4067cb6dcffSAndrew F. Davis ina3221_show_current, ina3221_set_current, INA3221_WARN1); 4077cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr2_max, S_IRUGO | S_IWUSR, 4087cb6dcffSAndrew F. Davis ina3221_show_current, ina3221_set_current, INA3221_WARN2); 4097cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr3_max, S_IRUGO | S_IWUSR, 4107cb6dcffSAndrew F. Davis ina3221_show_current, ina3221_set_current, INA3221_WARN3); 4117cb6dcffSAndrew F. Davis 4127cb6dcffSAndrew F. Davis /* warning current alert */ 4137cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, 4147cb6dcffSAndrew F. Davis ina3221_show_alert, NULL, F_WF1); 4157cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr2_max_alarm, S_IRUGO, 4167cb6dcffSAndrew F. Davis ina3221_show_alert, NULL, F_WF2); 4177cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr3_max_alarm, S_IRUGO, 4187cb6dcffSAndrew F. Davis ina3221_show_alert, NULL, F_WF3); 4197cb6dcffSAndrew F. Davis 4207cb6dcffSAndrew F. Davis /* shunt voltage */ 4217cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, 4227cb6dcffSAndrew F. Davis ina3221_show_shunt_voltage, NULL, INA3221_SHUNT1); 4237cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, 4247cb6dcffSAndrew F. Davis ina3221_show_shunt_voltage, NULL, INA3221_SHUNT2); 4257cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, 4267cb6dcffSAndrew F. Davis ina3221_show_shunt_voltage, NULL, INA3221_SHUNT3); 4277cb6dcffSAndrew F. Davis 4287cb6dcffSAndrew F. Davis static struct attribute *ina3221_attrs[] = { 429a9e9dd9cSNicolin Chen /* channel 1 -- make sure label at first */ 430a9e9dd9cSNicolin Chen &sensor_dev_attr_in1_label.dev_attr.attr, 431a9e9dd9cSNicolin Chen &sensor_dev_attr_in1_enable.dev_attr.attr, 4327cb6dcffSAndrew F. Davis &sensor_dev_attr_in1_input.dev_attr.attr, 4337cb6dcffSAndrew F. Davis &sensor_dev_attr_curr1_input.dev_attr.attr, 4347cb6dcffSAndrew F. Davis &sensor_dev_attr_shunt1_resistor.dev_attr.attr, 4357cb6dcffSAndrew F. Davis &sensor_dev_attr_curr1_crit.dev_attr.attr, 4367cb6dcffSAndrew F. Davis &sensor_dev_attr_curr1_crit_alarm.dev_attr.attr, 4377cb6dcffSAndrew F. Davis &sensor_dev_attr_curr1_max.dev_attr.attr, 4387cb6dcffSAndrew F. Davis &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, 4397cb6dcffSAndrew F. Davis &sensor_dev_attr_in4_input.dev_attr.attr, 4407cb6dcffSAndrew F. Davis 441a9e9dd9cSNicolin Chen /* channel 2 -- make sure label at first */ 442a9e9dd9cSNicolin Chen &sensor_dev_attr_in2_label.dev_attr.attr, 443a9e9dd9cSNicolin Chen &sensor_dev_attr_in2_enable.dev_attr.attr, 4447cb6dcffSAndrew F. Davis &sensor_dev_attr_in2_input.dev_attr.attr, 4457cb6dcffSAndrew F. Davis &sensor_dev_attr_curr2_input.dev_attr.attr, 4467cb6dcffSAndrew F. Davis &sensor_dev_attr_shunt2_resistor.dev_attr.attr, 4477cb6dcffSAndrew F. Davis &sensor_dev_attr_curr2_crit.dev_attr.attr, 4487cb6dcffSAndrew F. Davis &sensor_dev_attr_curr2_crit_alarm.dev_attr.attr, 4497cb6dcffSAndrew F. Davis &sensor_dev_attr_curr2_max.dev_attr.attr, 4507cb6dcffSAndrew F. Davis &sensor_dev_attr_curr2_max_alarm.dev_attr.attr, 4517cb6dcffSAndrew F. Davis &sensor_dev_attr_in5_input.dev_attr.attr, 4527cb6dcffSAndrew F. Davis 453a9e9dd9cSNicolin Chen /* channel 3 -- make sure label at first */ 454a9e9dd9cSNicolin Chen &sensor_dev_attr_in3_label.dev_attr.attr, 455a9e9dd9cSNicolin Chen &sensor_dev_attr_in3_enable.dev_attr.attr, 4567cb6dcffSAndrew F. Davis &sensor_dev_attr_in3_input.dev_attr.attr, 4577cb6dcffSAndrew F. Davis &sensor_dev_attr_curr3_input.dev_attr.attr, 4587cb6dcffSAndrew F. Davis &sensor_dev_attr_shunt3_resistor.dev_attr.attr, 4597cb6dcffSAndrew F. Davis &sensor_dev_attr_curr3_crit.dev_attr.attr, 4607cb6dcffSAndrew F. Davis &sensor_dev_attr_curr3_crit_alarm.dev_attr.attr, 4617cb6dcffSAndrew F. Davis &sensor_dev_attr_curr3_max.dev_attr.attr, 4627cb6dcffSAndrew F. Davis &sensor_dev_attr_curr3_max_alarm.dev_attr.attr, 4637cb6dcffSAndrew F. Davis &sensor_dev_attr_in6_input.dev_attr.attr, 4647cb6dcffSAndrew F. Davis 4657cb6dcffSAndrew F. Davis NULL, 4667cb6dcffSAndrew F. Davis }; 467a9e9dd9cSNicolin Chen 468a9e9dd9cSNicolin Chen static umode_t ina3221_attr_is_visible(struct kobject *kobj, 469a9e9dd9cSNicolin Chen struct attribute *attr, int n) 470a9e9dd9cSNicolin Chen { 471a9e9dd9cSNicolin Chen const int max_attrs = ARRAY_SIZE(ina3221_attrs) - 1; 472a9e9dd9cSNicolin Chen const int num_attrs = max_attrs / INA3221_NUM_CHANNELS; 473a9e9dd9cSNicolin Chen struct device *dev = kobj_to_dev(kobj); 474a9e9dd9cSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 475a9e9dd9cSNicolin Chen enum ina3221_channels channel = n / num_attrs; 476a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 477a9e9dd9cSNicolin Chen int index = n % num_attrs; 478a9e9dd9cSNicolin Chen 479a9e9dd9cSNicolin Chen /* Hide label node if label is not provided */ 480a9e9dd9cSNicolin Chen if (index == 0 && !input->label) 481a9e9dd9cSNicolin Chen return 0; 482a9e9dd9cSNicolin Chen 483a9e9dd9cSNicolin Chen return attr->mode; 484a9e9dd9cSNicolin Chen } 485a9e9dd9cSNicolin Chen 486a9e9dd9cSNicolin Chen static const struct attribute_group ina3221_group = { 487a9e9dd9cSNicolin Chen .is_visible = ina3221_attr_is_visible, 488a9e9dd9cSNicolin Chen .attrs = ina3221_attrs, 489a9e9dd9cSNicolin Chen }; 490a9e9dd9cSNicolin Chen __ATTRIBUTE_GROUPS(ina3221); 4917cb6dcffSAndrew F. Davis 4927cb6dcffSAndrew F. Davis static const struct regmap_range ina3221_yes_ranges[] = { 493c20217b3SNicolin Chen regmap_reg_range(INA3221_CONFIG, INA3221_BUS3), 4947cb6dcffSAndrew F. Davis regmap_reg_range(INA3221_MASK_ENABLE, INA3221_MASK_ENABLE), 4957cb6dcffSAndrew F. Davis }; 4967cb6dcffSAndrew F. Davis 4977cb6dcffSAndrew F. Davis static const struct regmap_access_table ina3221_volatile_table = { 4987cb6dcffSAndrew F. Davis .yes_ranges = ina3221_yes_ranges, 4997cb6dcffSAndrew F. Davis .n_yes_ranges = ARRAY_SIZE(ina3221_yes_ranges), 5007cb6dcffSAndrew F. Davis }; 5017cb6dcffSAndrew F. Davis 5027cb6dcffSAndrew F. Davis static const struct regmap_config ina3221_regmap_config = { 5037cb6dcffSAndrew F. Davis .reg_bits = 8, 5047cb6dcffSAndrew F. Davis .val_bits = 16, 5057cb6dcffSAndrew F. Davis 5067cb6dcffSAndrew F. Davis .cache_type = REGCACHE_RBTREE, 5077cb6dcffSAndrew F. Davis .volatile_table = &ina3221_volatile_table, 5087cb6dcffSAndrew F. Davis }; 5097cb6dcffSAndrew F. Davis 510a9e9dd9cSNicolin Chen static int ina3221_probe_child_from_dt(struct device *dev, 511a9e9dd9cSNicolin Chen struct device_node *child, 512a9e9dd9cSNicolin Chen struct ina3221_data *ina) 513a9e9dd9cSNicolin Chen { 514a9e9dd9cSNicolin Chen struct ina3221_input *input; 515a9e9dd9cSNicolin Chen u32 val; 516a9e9dd9cSNicolin Chen int ret; 517a9e9dd9cSNicolin Chen 518a9e9dd9cSNicolin Chen ret = of_property_read_u32(child, "reg", &val); 519a9e9dd9cSNicolin Chen if (ret) { 520a9e9dd9cSNicolin Chen dev_err(dev, "missing reg property of %s\n", child->name); 521a9e9dd9cSNicolin Chen return ret; 522a9e9dd9cSNicolin Chen } else if (val > INA3221_CHANNEL3) { 523a9e9dd9cSNicolin Chen dev_err(dev, "invalid reg %d of %s\n", val, child->name); 524a9e9dd9cSNicolin Chen return ret; 525a9e9dd9cSNicolin Chen } 526a9e9dd9cSNicolin Chen 527a9e9dd9cSNicolin Chen input = &ina->inputs[val]; 528a9e9dd9cSNicolin Chen 529a9e9dd9cSNicolin Chen /* Log the disconnected channel input */ 530a9e9dd9cSNicolin Chen if (!of_device_is_available(child)) { 531a9e9dd9cSNicolin Chen input->disconnected = true; 532a9e9dd9cSNicolin Chen return 0; 533a9e9dd9cSNicolin Chen } 534a9e9dd9cSNicolin Chen 535a9e9dd9cSNicolin Chen /* Save the connected input label if available */ 536a9e9dd9cSNicolin Chen of_property_read_string(child, "label", &input->label); 537a9e9dd9cSNicolin Chen 538a9e9dd9cSNicolin Chen /* Overwrite default shunt resistor value optionally */ 539a9e9dd9cSNicolin Chen if (!of_property_read_u32(child, "shunt-resistor-micro-ohms", &val)) 540a9e9dd9cSNicolin Chen input->shunt_resistor = val; 541a9e9dd9cSNicolin Chen 542a9e9dd9cSNicolin Chen return 0; 543a9e9dd9cSNicolin Chen } 544a9e9dd9cSNicolin Chen 545a9e9dd9cSNicolin Chen static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina) 546a9e9dd9cSNicolin Chen { 547a9e9dd9cSNicolin Chen const struct device_node *np = dev->of_node; 548a9e9dd9cSNicolin Chen struct device_node *child; 549a9e9dd9cSNicolin Chen int ret; 550a9e9dd9cSNicolin Chen 551a9e9dd9cSNicolin Chen /* Compatible with non-DT platforms */ 552a9e9dd9cSNicolin Chen if (!np) 553a9e9dd9cSNicolin Chen return 0; 554a9e9dd9cSNicolin Chen 555a9e9dd9cSNicolin Chen for_each_child_of_node(np, child) { 556a9e9dd9cSNicolin Chen ret = ina3221_probe_child_from_dt(dev, child, ina); 557a9e9dd9cSNicolin Chen if (ret) 558a9e9dd9cSNicolin Chen return ret; 559a9e9dd9cSNicolin Chen } 560a9e9dd9cSNicolin Chen 561a9e9dd9cSNicolin Chen return 0; 562a9e9dd9cSNicolin Chen } 563a9e9dd9cSNicolin Chen 5647cb6dcffSAndrew F. Davis static int ina3221_probe(struct i2c_client *client, 5657cb6dcffSAndrew F. Davis const struct i2c_device_id *id) 5667cb6dcffSAndrew F. Davis { 5677cb6dcffSAndrew F. Davis struct device *dev = &client->dev; 5687cb6dcffSAndrew F. Davis struct ina3221_data *ina; 5697cb6dcffSAndrew F. Davis struct device *hwmon_dev; 5707cb6dcffSAndrew F. Davis int i, ret; 5717cb6dcffSAndrew F. Davis 5727cb6dcffSAndrew F. Davis ina = devm_kzalloc(dev, sizeof(*ina), GFP_KERNEL); 5737cb6dcffSAndrew F. Davis if (!ina) 5747cb6dcffSAndrew F. Davis return -ENOMEM; 5757cb6dcffSAndrew F. Davis 5767cb6dcffSAndrew F. Davis ina->regmap = devm_regmap_init_i2c(client, &ina3221_regmap_config); 5777cb6dcffSAndrew F. Davis if (IS_ERR(ina->regmap)) { 5787cb6dcffSAndrew F. Davis dev_err(dev, "Unable to allocate register map\n"); 5797cb6dcffSAndrew F. Davis return PTR_ERR(ina->regmap); 5807cb6dcffSAndrew F. Davis } 5817cb6dcffSAndrew F. Davis 5827cb6dcffSAndrew F. Davis for (i = 0; i < F_MAX_FIELDS; i++) { 5837cb6dcffSAndrew F. Davis ina->fields[i] = devm_regmap_field_alloc(dev, 5847cb6dcffSAndrew F. Davis ina->regmap, 5857cb6dcffSAndrew F. Davis ina3221_reg_fields[i]); 5867cb6dcffSAndrew F. Davis if (IS_ERR(ina->fields[i])) { 5877cb6dcffSAndrew F. Davis dev_err(dev, "Unable to allocate regmap fields\n"); 5887cb6dcffSAndrew F. Davis return PTR_ERR(ina->fields[i]); 5897cb6dcffSAndrew F. Davis } 5907cb6dcffSAndrew F. Davis } 5917cb6dcffSAndrew F. Davis 5927cb6dcffSAndrew F. Davis for (i = 0; i < INA3221_NUM_CHANNELS; i++) 593a9e9dd9cSNicolin Chen ina->inputs[i].shunt_resistor = INA3221_RSHUNT_DEFAULT; 594a9e9dd9cSNicolin Chen 595a9e9dd9cSNicolin Chen ret = ina3221_probe_from_dt(dev, ina); 596a9e9dd9cSNicolin Chen if (ret) { 597a9e9dd9cSNicolin Chen dev_err(dev, "Unable to probe from device tree\n"); 598a9e9dd9cSNicolin Chen return ret; 599a9e9dd9cSNicolin Chen } 6007cb6dcffSAndrew F. Davis 6017cb6dcffSAndrew F. Davis ret = regmap_field_write(ina->fields[F_RST], true); 6027cb6dcffSAndrew F. Davis if (ret) { 6037cb6dcffSAndrew F. Davis dev_err(dev, "Unable to reset device\n"); 6047cb6dcffSAndrew F. Davis return ret; 6057cb6dcffSAndrew F. Davis } 6067cb6dcffSAndrew F. Davis 607a9e9dd9cSNicolin Chen /* Sync config register after reset */ 608a9e9dd9cSNicolin Chen ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config); 609a9e9dd9cSNicolin Chen if (ret) 610a9e9dd9cSNicolin Chen return ret; 611a9e9dd9cSNicolin Chen 612a9e9dd9cSNicolin Chen /* Disable channels if their inputs are disconnected */ 613a9e9dd9cSNicolin Chen for (i = 0; i < INA3221_NUM_CHANNELS; i++) { 614a9e9dd9cSNicolin Chen if (ina->inputs[i].disconnected) 615a9e9dd9cSNicolin Chen ina->reg_config &= ~INA3221_CONFIG_CHx_EN(i); 616a9e9dd9cSNicolin Chen } 617a9e9dd9cSNicolin Chen ret = regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config); 618a9e9dd9cSNicolin Chen if (ret) 619a9e9dd9cSNicolin Chen return ret; 620a9e9dd9cSNicolin Chen 62159d608e1SNicolin Chen dev_set_drvdata(dev, ina); 62259d608e1SNicolin Chen 6237cb6dcffSAndrew F. Davis hwmon_dev = devm_hwmon_device_register_with_groups(dev, 6247cb6dcffSAndrew F. Davis client->name, 6257cb6dcffSAndrew F. Davis ina, ina3221_groups); 6267cb6dcffSAndrew F. Davis if (IS_ERR(hwmon_dev)) { 6277cb6dcffSAndrew F. Davis dev_err(dev, "Unable to register hwmon device\n"); 6287cb6dcffSAndrew F. Davis return PTR_ERR(hwmon_dev); 6297cb6dcffSAndrew F. Davis } 6307cb6dcffSAndrew F. Davis 6317cb6dcffSAndrew F. Davis return 0; 6327cb6dcffSAndrew F. Davis } 6337cb6dcffSAndrew F. Davis 634ead21c77SArnd Bergmann static int __maybe_unused ina3221_suspend(struct device *dev) 63559d608e1SNicolin Chen { 63659d608e1SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 63759d608e1SNicolin Chen int ret; 63859d608e1SNicolin Chen 63959d608e1SNicolin Chen /* Save config register value and enable cache-only */ 64059d608e1SNicolin Chen ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config); 64159d608e1SNicolin Chen if (ret) 64259d608e1SNicolin Chen return ret; 64359d608e1SNicolin Chen 64459d608e1SNicolin Chen /* Set to power-down mode for power saving */ 64559d608e1SNicolin Chen ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, 64659d608e1SNicolin Chen INA3221_CONFIG_MODE_MASK, 64759d608e1SNicolin Chen INA3221_CONFIG_MODE_POWERDOWN); 64859d608e1SNicolin Chen if (ret) 64959d608e1SNicolin Chen return ret; 65059d608e1SNicolin Chen 65159d608e1SNicolin Chen regcache_cache_only(ina->regmap, true); 65259d608e1SNicolin Chen regcache_mark_dirty(ina->regmap); 65359d608e1SNicolin Chen 65459d608e1SNicolin Chen return 0; 65559d608e1SNicolin Chen } 65659d608e1SNicolin Chen 657ead21c77SArnd Bergmann static int __maybe_unused ina3221_resume(struct device *dev) 65859d608e1SNicolin Chen { 65959d608e1SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 66059d608e1SNicolin Chen int ret; 66159d608e1SNicolin Chen 66259d608e1SNicolin Chen regcache_cache_only(ina->regmap, false); 66359d608e1SNicolin Chen 66459d608e1SNicolin Chen /* Software reset the chip */ 66559d608e1SNicolin Chen ret = regmap_field_write(ina->fields[F_RST], true); 66659d608e1SNicolin Chen if (ret) { 66759d608e1SNicolin Chen dev_err(dev, "Unable to reset device\n"); 66859d608e1SNicolin Chen return ret; 66959d608e1SNicolin Chen } 67059d608e1SNicolin Chen 67159d608e1SNicolin Chen /* Restore cached register values to hardware */ 67259d608e1SNicolin Chen ret = regcache_sync(ina->regmap); 67359d608e1SNicolin Chen if (ret) 67459d608e1SNicolin Chen return ret; 67559d608e1SNicolin Chen 67659d608e1SNicolin Chen /* Restore config register value to hardware */ 67759d608e1SNicolin Chen ret = regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config); 67859d608e1SNicolin Chen if (ret) 67959d608e1SNicolin Chen return ret; 68059d608e1SNicolin Chen 68159d608e1SNicolin Chen return 0; 68259d608e1SNicolin Chen } 68359d608e1SNicolin Chen 68459d608e1SNicolin Chen static const struct dev_pm_ops ina3221_pm = { 68559d608e1SNicolin Chen SET_SYSTEM_SLEEP_PM_OPS(ina3221_suspend, ina3221_resume) 68659d608e1SNicolin Chen }; 68759d608e1SNicolin Chen 6887cb6dcffSAndrew F. Davis static const struct of_device_id ina3221_of_match_table[] = { 6897cb6dcffSAndrew F. Davis { .compatible = "ti,ina3221", }, 6907cb6dcffSAndrew F. Davis { /* sentinel */ } 6917cb6dcffSAndrew F. Davis }; 6927cb6dcffSAndrew F. Davis MODULE_DEVICE_TABLE(of, ina3221_of_match_table); 6937cb6dcffSAndrew F. Davis 6947cb6dcffSAndrew F. Davis static const struct i2c_device_id ina3221_ids[] = { 6957cb6dcffSAndrew F. Davis { "ina3221", 0 }, 6967cb6dcffSAndrew F. Davis { /* sentinel */ } 6977cb6dcffSAndrew F. Davis }; 6987cb6dcffSAndrew F. Davis MODULE_DEVICE_TABLE(i2c, ina3221_ids); 6997cb6dcffSAndrew F. Davis 7007cb6dcffSAndrew F. Davis static struct i2c_driver ina3221_i2c_driver = { 7017cb6dcffSAndrew F. Davis .probe = ina3221_probe, 7027cb6dcffSAndrew F. Davis .driver = { 7037cb6dcffSAndrew F. Davis .name = INA3221_DRIVER_NAME, 7047cb6dcffSAndrew F. Davis .of_match_table = ina3221_of_match_table, 70559d608e1SNicolin Chen .pm = &ina3221_pm, 7067cb6dcffSAndrew F. Davis }, 7077cb6dcffSAndrew F. Davis .id_table = ina3221_ids, 7087cb6dcffSAndrew F. Davis }; 7097cb6dcffSAndrew F. Davis module_i2c_driver(ina3221_i2c_driver); 7107cb6dcffSAndrew F. Davis 7117cb6dcffSAndrew F. Davis MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); 7127cb6dcffSAndrew F. Davis MODULE_DESCRIPTION("Texas Instruments INA3221 HWMon Driver"); 7137cb6dcffSAndrew F. Davis MODULE_LICENSE("GPL v2"); 714