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 /** 81a9e9dd9cSNicolin Chen * struct ina3221_input - channel input source specific information 82a9e9dd9cSNicolin Chen * @label: label of channel input source 83a9e9dd9cSNicolin Chen * @shunt_resistor: shunt resistor value of channel input source 84a9e9dd9cSNicolin Chen * @disconnected: connection status of channel input source 85a9e9dd9cSNicolin Chen */ 86a9e9dd9cSNicolin Chen struct ina3221_input { 87a9e9dd9cSNicolin Chen const char *label; 88a9e9dd9cSNicolin Chen int shunt_resistor; 89a9e9dd9cSNicolin Chen bool disconnected; 90a9e9dd9cSNicolin Chen }; 91a9e9dd9cSNicolin Chen 92a9e9dd9cSNicolin Chen /** 937cb6dcffSAndrew F. Davis * struct ina3221_data - device specific information 947cb6dcffSAndrew F. Davis * @regmap: Register map of the device 957cb6dcffSAndrew F. Davis * @fields: Register fields of the device 96a9e9dd9cSNicolin Chen * @inputs: Array of channel input source specific structures 9759d608e1SNicolin Chen * @reg_config: Register value of INA3221_CONFIG 987cb6dcffSAndrew F. Davis */ 997cb6dcffSAndrew F. Davis struct ina3221_data { 1007cb6dcffSAndrew F. Davis struct regmap *regmap; 1017cb6dcffSAndrew F. Davis struct regmap_field *fields[F_MAX_FIELDS]; 102a9e9dd9cSNicolin Chen struct ina3221_input inputs[INA3221_NUM_CHANNELS]; 10359d608e1SNicolin Chen u32 reg_config; 1047cb6dcffSAndrew F. Davis }; 1057cb6dcffSAndrew F. Davis 106a9e9dd9cSNicolin Chen static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel) 107a9e9dd9cSNicolin Chen { 108a9e9dd9cSNicolin Chen return ina->reg_config & INA3221_CONFIG_CHx_EN(channel); 109a9e9dd9cSNicolin Chen } 110a9e9dd9cSNicolin Chen 1117cb6dcffSAndrew F. Davis static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg, 1127cb6dcffSAndrew F. Davis int *val) 1137cb6dcffSAndrew F. Davis { 1147cb6dcffSAndrew F. Davis unsigned int regval; 1157cb6dcffSAndrew F. Davis int ret; 1167cb6dcffSAndrew F. Davis 1177cb6dcffSAndrew F. Davis ret = regmap_read(ina->regmap, reg, ®val); 1187cb6dcffSAndrew F. Davis if (ret) 1197cb6dcffSAndrew F. Davis return ret; 1207cb6dcffSAndrew F. Davis 1217cb6dcffSAndrew F. Davis *val = sign_extend32(regval >> 3, 12); 1227cb6dcffSAndrew F. Davis 1237cb6dcffSAndrew F. Davis return 0; 1247cb6dcffSAndrew F. Davis } 1257cb6dcffSAndrew F. Davis 126d4b0166dSNicolin Chen static const u8 ina3221_in_reg[] = { 127d4b0166dSNicolin Chen INA3221_BUS1, 128d4b0166dSNicolin Chen INA3221_BUS2, 129d4b0166dSNicolin Chen INA3221_BUS3, 130d4b0166dSNicolin Chen INA3221_SHUNT1, 131d4b0166dSNicolin Chen INA3221_SHUNT2, 132d4b0166dSNicolin Chen INA3221_SHUNT3, 133d4b0166dSNicolin Chen }; 1347cb6dcffSAndrew F. Davis 135d4b0166dSNicolin Chen static int ina3221_read_in(struct device *dev, u32 attr, int channel, long *val) 136d4b0166dSNicolin Chen { 137d4b0166dSNicolin Chen const bool is_shunt = channel > INA3221_CHANNEL3; 138d4b0166dSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 139d4b0166dSNicolin Chen u8 reg = ina3221_in_reg[channel]; 140d4b0166dSNicolin Chen int regval, ret; 141d4b0166dSNicolin Chen 142d4b0166dSNicolin Chen /* Translate shunt channel index to sensor channel index */ 143d4b0166dSNicolin Chen channel %= INA3221_NUM_CHANNELS; 144d4b0166dSNicolin Chen 145d4b0166dSNicolin Chen switch (attr) { 146d4b0166dSNicolin Chen case hwmon_in_input: 147d4b0166dSNicolin Chen if (!ina3221_is_enabled(ina, channel)) 148a9e9dd9cSNicolin Chen return -ENODATA; 149a9e9dd9cSNicolin Chen 150d4b0166dSNicolin Chen ret = ina3221_read_value(ina, reg, ®val); 1517cb6dcffSAndrew F. Davis if (ret) 1527cb6dcffSAndrew F. Davis return ret; 1537cb6dcffSAndrew F. Davis 154d4b0166dSNicolin Chen /* 155d4b0166dSNicolin Chen * Scale of shunt voltage (uV): LSB is 40uV 156d4b0166dSNicolin Chen * Scale of bus voltage (mV): LSB is 8mV 157d4b0166dSNicolin Chen */ 158d4b0166dSNicolin Chen *val = regval * (is_shunt ? 40 : 8); 159d4b0166dSNicolin Chen return 0; 160d4b0166dSNicolin Chen case hwmon_in_enable: 161d4b0166dSNicolin Chen *val = ina3221_is_enabled(ina, channel); 162d4b0166dSNicolin Chen return 0; 163d4b0166dSNicolin Chen default: 164d4b0166dSNicolin Chen return -EOPNOTSUPP; 165d4b0166dSNicolin Chen } 1667cb6dcffSAndrew F. Davis } 1677cb6dcffSAndrew F. Davis 168d4b0166dSNicolin Chen static const u8 ina3221_curr_reg[][INA3221_NUM_CHANNELS] = { 169d4b0166dSNicolin Chen [hwmon_curr_input] = { INA3221_SHUNT1, INA3221_SHUNT2, INA3221_SHUNT3 }, 170d4b0166dSNicolin Chen [hwmon_curr_max] = { INA3221_WARN1, INA3221_WARN2, INA3221_WARN3 }, 171d4b0166dSNicolin Chen [hwmon_curr_crit] = { INA3221_CRIT1, INA3221_CRIT2, INA3221_CRIT3 }, 172d4b0166dSNicolin Chen [hwmon_curr_max_alarm] = { F_WF1, F_WF2, F_WF3 }, 173d4b0166dSNicolin Chen [hwmon_curr_crit_alarm] = { F_CF1, F_CF2, F_CF3 }, 174d4b0166dSNicolin Chen }; 175d4b0166dSNicolin Chen 176d4b0166dSNicolin Chen static int ina3221_read_curr(struct device *dev, u32 attr, 177d4b0166dSNicolin Chen int channel, long *val) 1787cb6dcffSAndrew F. Davis { 1797cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 180a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 181a9e9dd9cSNicolin Chen int resistance_uo = input->shunt_resistor; 182d4b0166dSNicolin Chen u8 reg = ina3221_curr_reg[attr][channel]; 183d4b0166dSNicolin Chen int regval, voltage_nv, ret; 1847cb6dcffSAndrew F. Davis 185d4b0166dSNicolin Chen switch (attr) { 186d4b0166dSNicolin Chen case hwmon_curr_input: 187d4b0166dSNicolin Chen if (!ina3221_is_enabled(ina, channel)) 188a9e9dd9cSNicolin Chen return -ENODATA; 189d4b0166dSNicolin Chen /* fall through */ 190d4b0166dSNicolin Chen case hwmon_curr_crit: 191d4b0166dSNicolin Chen case hwmon_curr_max: 192d4b0166dSNicolin Chen ret = ina3221_read_value(ina, reg, ®val); 1937cb6dcffSAndrew F. Davis if (ret) 1947cb6dcffSAndrew F. Davis return ret; 1957cb6dcffSAndrew F. Davis 196d4b0166dSNicolin Chen /* Scale of shunt voltage: LSB is 40uV (40000nV) */ 197d4b0166dSNicolin Chen voltage_nv = regval * 40000; 198d4b0166dSNicolin Chen /* Return current in mA */ 199d4b0166dSNicolin Chen *val = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo); 200d4b0166dSNicolin Chen return 0; 201d4b0166dSNicolin Chen case hwmon_curr_crit_alarm: 202d4b0166dSNicolin Chen case hwmon_curr_max_alarm: 203efb0489eSNicolin Chen /* No actual register read if channel is disabled */ 204efb0489eSNicolin Chen if (!ina3221_is_enabled(ina, channel)) { 205efb0489eSNicolin Chen /* Return 0 for alert flags */ 206efb0489eSNicolin Chen *val = 0; 207efb0489eSNicolin Chen return 0; 208efb0489eSNicolin Chen } 209d4b0166dSNicolin Chen ret = regmap_field_read(ina->fields[reg], ®val); 210d4b0166dSNicolin Chen if (ret) 211d4b0166dSNicolin Chen return ret; 212d4b0166dSNicolin Chen *val = regval; 213d4b0166dSNicolin Chen return 0; 214d4b0166dSNicolin Chen default: 215d4b0166dSNicolin Chen return -EOPNOTSUPP; 216d4b0166dSNicolin Chen } 2177cb6dcffSAndrew F. Davis } 2187cb6dcffSAndrew F. Davis 219d4b0166dSNicolin Chen static int ina3221_write_curr(struct device *dev, u32 attr, 220d4b0166dSNicolin Chen int channel, long val) 2217cb6dcffSAndrew F. Davis { 2227cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 223a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 224a9e9dd9cSNicolin Chen int resistance_uo = input->shunt_resistor; 225d4b0166dSNicolin Chen u8 reg = ina3221_curr_reg[attr][channel]; 226d4b0166dSNicolin Chen int regval, current_ma, voltage_uv; 2277cb6dcffSAndrew F. Davis 2287cb6dcffSAndrew F. Davis /* clamp current */ 229d4b0166dSNicolin Chen current_ma = clamp_val(val, 2307cb6dcffSAndrew F. Davis INT_MIN / resistance_uo, 2317cb6dcffSAndrew F. Davis INT_MAX / resistance_uo); 2327cb6dcffSAndrew F. Davis 2337cb6dcffSAndrew F. Davis voltage_uv = DIV_ROUND_CLOSEST(current_ma * resistance_uo, 1000); 2347cb6dcffSAndrew F. Davis 2357cb6dcffSAndrew F. Davis /* clamp voltage */ 2367cb6dcffSAndrew F. Davis voltage_uv = clamp_val(voltage_uv, -163800, 163800); 2377cb6dcffSAndrew F. Davis 2387cb6dcffSAndrew F. Davis /* 1 / 40uV(scale) << 3(register shift) = 5 */ 239d4b0166dSNicolin Chen regval = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8; 2407cb6dcffSAndrew F. Davis 241d4b0166dSNicolin Chen return regmap_write(ina->regmap, reg, regval); 242d4b0166dSNicolin Chen } 243d4b0166dSNicolin Chen 244d4b0166dSNicolin Chen static int ina3221_write_enable(struct device *dev, int channel, bool enable) 245d4b0166dSNicolin Chen { 246d4b0166dSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 247d4b0166dSNicolin Chen u16 config, mask = INA3221_CONFIG_CHx_EN(channel); 248d4b0166dSNicolin Chen int ret; 249d4b0166dSNicolin Chen 250d4b0166dSNicolin Chen config = enable ? mask : 0; 251d4b0166dSNicolin Chen 252d4b0166dSNicolin Chen /* Enable or disable the channel */ 253d4b0166dSNicolin Chen ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, mask, config); 2547cb6dcffSAndrew F. Davis if (ret) 2557cb6dcffSAndrew F. Davis return ret; 2567cb6dcffSAndrew F. Davis 257d4b0166dSNicolin Chen /* Cache the latest config register value */ 258d4b0166dSNicolin Chen ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config); 259d4b0166dSNicolin Chen if (ret) 260d4b0166dSNicolin Chen return ret; 261d4b0166dSNicolin Chen 262d4b0166dSNicolin Chen return 0; 2637cb6dcffSAndrew F. Davis } 2647cb6dcffSAndrew F. Davis 265d4b0166dSNicolin Chen static int ina3221_read(struct device *dev, enum hwmon_sensor_types type, 266d4b0166dSNicolin Chen u32 attr, int channel, long *val) 267d4b0166dSNicolin Chen { 268d4b0166dSNicolin Chen switch (type) { 269d4b0166dSNicolin Chen case hwmon_in: 270d4b0166dSNicolin Chen /* 0-align channel ID */ 271d4b0166dSNicolin Chen return ina3221_read_in(dev, attr, channel - 1, val); 272d4b0166dSNicolin Chen case hwmon_curr: 273d4b0166dSNicolin Chen return ina3221_read_curr(dev, attr, channel, val); 274d4b0166dSNicolin Chen default: 275d4b0166dSNicolin Chen return -EOPNOTSUPP; 276d4b0166dSNicolin Chen } 277d4b0166dSNicolin Chen } 278d4b0166dSNicolin Chen 279d4b0166dSNicolin Chen static int ina3221_write(struct device *dev, enum hwmon_sensor_types type, 280d4b0166dSNicolin Chen u32 attr, int channel, long val) 281d4b0166dSNicolin Chen { 282d4b0166dSNicolin Chen switch (type) { 283d4b0166dSNicolin Chen case hwmon_in: 284d4b0166dSNicolin Chen /* 0-align channel ID */ 285d4b0166dSNicolin Chen return ina3221_write_enable(dev, channel - 1, val); 286d4b0166dSNicolin Chen case hwmon_curr: 287d4b0166dSNicolin Chen return ina3221_write_curr(dev, attr, channel, val); 288d4b0166dSNicolin Chen default: 289d4b0166dSNicolin Chen return -EOPNOTSUPP; 290d4b0166dSNicolin Chen } 291d4b0166dSNicolin Chen } 292d4b0166dSNicolin Chen 293d4b0166dSNicolin Chen static int ina3221_read_string(struct device *dev, enum hwmon_sensor_types type, 294d4b0166dSNicolin Chen u32 attr, int channel, const char **str) 295d4b0166dSNicolin Chen { 296d4b0166dSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 297d4b0166dSNicolin Chen int index = channel - 1; 298d4b0166dSNicolin Chen 299d4b0166dSNicolin Chen *str = ina->inputs[index].label; 300d4b0166dSNicolin Chen 301d4b0166dSNicolin Chen return 0; 302d4b0166dSNicolin Chen } 303d4b0166dSNicolin Chen 304d4b0166dSNicolin Chen static umode_t ina3221_is_visible(const void *drvdata, 305d4b0166dSNicolin Chen enum hwmon_sensor_types type, 306d4b0166dSNicolin Chen u32 attr, int channel) 307d4b0166dSNicolin Chen { 308d4b0166dSNicolin Chen const struct ina3221_data *ina = drvdata; 309d4b0166dSNicolin Chen const struct ina3221_input *input = NULL; 310d4b0166dSNicolin Chen 311d4b0166dSNicolin Chen switch (type) { 312d4b0166dSNicolin Chen case hwmon_in: 313d4b0166dSNicolin Chen /* Ignore in0_ */ 314d4b0166dSNicolin Chen if (channel == 0) 315d4b0166dSNicolin Chen return 0; 316d4b0166dSNicolin Chen 317d4b0166dSNicolin Chen switch (attr) { 318d4b0166dSNicolin Chen case hwmon_in_label: 319d4b0166dSNicolin Chen if (channel - 1 <= INA3221_CHANNEL3) 320d4b0166dSNicolin Chen input = &ina->inputs[channel - 1]; 321d4b0166dSNicolin Chen /* Hide label node if label is not provided */ 322d4b0166dSNicolin Chen return (input && input->label) ? 0444 : 0; 323d4b0166dSNicolin Chen case hwmon_in_input: 324d4b0166dSNicolin Chen return 0444; 325d4b0166dSNicolin Chen case hwmon_in_enable: 326d4b0166dSNicolin Chen return 0644; 327d4b0166dSNicolin Chen default: 328d4b0166dSNicolin Chen return 0; 329d4b0166dSNicolin Chen } 330d4b0166dSNicolin Chen case hwmon_curr: 331d4b0166dSNicolin Chen switch (attr) { 332d4b0166dSNicolin Chen case hwmon_curr_input: 333d4b0166dSNicolin Chen case hwmon_curr_crit_alarm: 334d4b0166dSNicolin Chen case hwmon_curr_max_alarm: 335d4b0166dSNicolin Chen return 0444; 336d4b0166dSNicolin Chen case hwmon_curr_crit: 337d4b0166dSNicolin Chen case hwmon_curr_max: 338d4b0166dSNicolin Chen return 0644; 339d4b0166dSNicolin Chen default: 340d4b0166dSNicolin Chen return 0; 341d4b0166dSNicolin Chen } 342d4b0166dSNicolin Chen default: 343d4b0166dSNicolin Chen return 0; 344d4b0166dSNicolin Chen } 345d4b0166dSNicolin Chen } 346d4b0166dSNicolin Chen 347d4b0166dSNicolin Chen static const u32 ina3221_in_config[] = { 348d4b0166dSNicolin Chen /* 0: dummy, skipped in is_visible */ 349d4b0166dSNicolin Chen HWMON_I_INPUT, 350d4b0166dSNicolin Chen /* 1-3: input voltage Channels */ 351d4b0166dSNicolin Chen HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, 352d4b0166dSNicolin Chen HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, 353d4b0166dSNicolin Chen HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, 354d4b0166dSNicolin Chen /* 4-6: shunt voltage Channels */ 355d4b0166dSNicolin Chen HWMON_I_INPUT, 356d4b0166dSNicolin Chen HWMON_I_INPUT, 357d4b0166dSNicolin Chen HWMON_I_INPUT, 358d4b0166dSNicolin Chen 0 359d4b0166dSNicolin Chen }; 360d4b0166dSNicolin Chen 361d4b0166dSNicolin Chen static const struct hwmon_channel_info ina3221_in = { 362d4b0166dSNicolin Chen .type = hwmon_in, 363d4b0166dSNicolin Chen .config = ina3221_in_config, 364d4b0166dSNicolin Chen }; 365d4b0166dSNicolin Chen 366d4b0166dSNicolin Chen #define INA3221_HWMON_CURR_CONFIG (HWMON_C_INPUT | \ 367d4b0166dSNicolin Chen HWMON_C_CRIT | HWMON_C_CRIT_ALARM | \ 368d4b0166dSNicolin Chen HWMON_C_MAX | HWMON_C_MAX_ALARM) 369d4b0166dSNicolin Chen 370d4b0166dSNicolin Chen static const u32 ina3221_curr_config[] = { 371d4b0166dSNicolin Chen INA3221_HWMON_CURR_CONFIG, 372d4b0166dSNicolin Chen INA3221_HWMON_CURR_CONFIG, 373d4b0166dSNicolin Chen INA3221_HWMON_CURR_CONFIG, 374d4b0166dSNicolin Chen 0 375d4b0166dSNicolin Chen }; 376d4b0166dSNicolin Chen 377d4b0166dSNicolin Chen static const struct hwmon_channel_info ina3221_curr = { 378d4b0166dSNicolin Chen .type = hwmon_curr, 379d4b0166dSNicolin Chen .config = ina3221_curr_config, 380d4b0166dSNicolin Chen }; 381d4b0166dSNicolin Chen 382d4b0166dSNicolin Chen static const struct hwmon_channel_info *ina3221_info[] = { 383d4b0166dSNicolin Chen &ina3221_in, 384d4b0166dSNicolin Chen &ina3221_curr, 385d4b0166dSNicolin Chen NULL 386d4b0166dSNicolin Chen }; 387d4b0166dSNicolin Chen 388d4b0166dSNicolin Chen static const struct hwmon_ops ina3221_hwmon_ops = { 389d4b0166dSNicolin Chen .is_visible = ina3221_is_visible, 390d4b0166dSNicolin Chen .read_string = ina3221_read_string, 391d4b0166dSNicolin Chen .read = ina3221_read, 392d4b0166dSNicolin Chen .write = ina3221_write, 393d4b0166dSNicolin Chen }; 394d4b0166dSNicolin Chen 395d4b0166dSNicolin Chen static const struct hwmon_chip_info ina3221_chip_info = { 396d4b0166dSNicolin Chen .ops = &ina3221_hwmon_ops, 397d4b0166dSNicolin Chen .info = ina3221_info, 398d4b0166dSNicolin Chen }; 399d4b0166dSNicolin Chen 400d4b0166dSNicolin Chen /* Extra attribute groups */ 4017cb6dcffSAndrew F. Davis static ssize_t ina3221_show_shunt(struct device *dev, 4027cb6dcffSAndrew F. Davis struct device_attribute *attr, char *buf) 4037cb6dcffSAndrew F. Davis { 4047cb6dcffSAndrew F. Davis struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); 4057cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 4067cb6dcffSAndrew F. Davis unsigned int channel = sd_attr->index; 407a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 4087cb6dcffSAndrew F. Davis 409a9e9dd9cSNicolin Chen return snprintf(buf, PAGE_SIZE, "%d\n", input->shunt_resistor); 4107cb6dcffSAndrew F. Davis } 4117cb6dcffSAndrew F. Davis 4127cb6dcffSAndrew F. Davis static ssize_t ina3221_set_shunt(struct device *dev, 4137cb6dcffSAndrew F. Davis struct device_attribute *attr, 4147cb6dcffSAndrew F. Davis const char *buf, size_t count) 4157cb6dcffSAndrew F. Davis { 4167cb6dcffSAndrew F. Davis struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); 4177cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 4187cb6dcffSAndrew F. Davis unsigned int channel = sd_attr->index; 419a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 4209ad0df1aSGuenter Roeck int val; 4217cb6dcffSAndrew F. Davis int ret; 4227cb6dcffSAndrew F. Davis 4239ad0df1aSGuenter Roeck ret = kstrtoint(buf, 0, &val); 4247cb6dcffSAndrew F. Davis if (ret) 4257cb6dcffSAndrew F. Davis return ret; 4267cb6dcffSAndrew F. Davis 4279ad0df1aSGuenter Roeck val = clamp_val(val, 1, INT_MAX); 4287cb6dcffSAndrew F. Davis 429a9e9dd9cSNicolin Chen input->shunt_resistor = val; 4307cb6dcffSAndrew F. Davis 4317cb6dcffSAndrew F. Davis return count; 4327cb6dcffSAndrew F. Davis } 4337cb6dcffSAndrew F. Davis 4347cb6dcffSAndrew F. Davis /* shunt resistance */ 4357cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(shunt1_resistor, S_IRUGO | S_IWUSR, 4367cb6dcffSAndrew F. Davis ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL1); 4377cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(shunt2_resistor, S_IRUGO | S_IWUSR, 4387cb6dcffSAndrew F. Davis ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL2); 4397cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(shunt3_resistor, S_IRUGO | S_IWUSR, 4407cb6dcffSAndrew F. Davis ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL3); 4417cb6dcffSAndrew F. Davis 4427cb6dcffSAndrew F. Davis static struct attribute *ina3221_attrs[] = { 4437cb6dcffSAndrew F. Davis &sensor_dev_attr_shunt1_resistor.dev_attr.attr, 4447cb6dcffSAndrew F. Davis &sensor_dev_attr_shunt2_resistor.dev_attr.attr, 4457cb6dcffSAndrew F. Davis &sensor_dev_attr_shunt3_resistor.dev_attr.attr, 4467cb6dcffSAndrew F. Davis NULL, 4477cb6dcffSAndrew F. Davis }; 448d4b0166dSNicolin Chen ATTRIBUTE_GROUPS(ina3221); 4497cb6dcffSAndrew F. Davis 4507cb6dcffSAndrew F. Davis static const struct regmap_range ina3221_yes_ranges[] = { 451c20217b3SNicolin Chen regmap_reg_range(INA3221_CONFIG, INA3221_BUS3), 4527cb6dcffSAndrew F. Davis regmap_reg_range(INA3221_MASK_ENABLE, INA3221_MASK_ENABLE), 4537cb6dcffSAndrew F. Davis }; 4547cb6dcffSAndrew F. Davis 4557cb6dcffSAndrew F. Davis static const struct regmap_access_table ina3221_volatile_table = { 4567cb6dcffSAndrew F. Davis .yes_ranges = ina3221_yes_ranges, 4577cb6dcffSAndrew F. Davis .n_yes_ranges = ARRAY_SIZE(ina3221_yes_ranges), 4587cb6dcffSAndrew F. Davis }; 4597cb6dcffSAndrew F. Davis 4607cb6dcffSAndrew F. Davis static const struct regmap_config ina3221_regmap_config = { 4617cb6dcffSAndrew F. Davis .reg_bits = 8, 4627cb6dcffSAndrew F. Davis .val_bits = 16, 4637cb6dcffSAndrew F. Davis 4647cb6dcffSAndrew F. Davis .cache_type = REGCACHE_RBTREE, 4657cb6dcffSAndrew F. Davis .volatile_table = &ina3221_volatile_table, 4667cb6dcffSAndrew F. Davis }; 4677cb6dcffSAndrew F. Davis 468a9e9dd9cSNicolin Chen static int ina3221_probe_child_from_dt(struct device *dev, 469a9e9dd9cSNicolin Chen struct device_node *child, 470a9e9dd9cSNicolin Chen struct ina3221_data *ina) 471a9e9dd9cSNicolin Chen { 472a9e9dd9cSNicolin Chen struct ina3221_input *input; 473a9e9dd9cSNicolin Chen u32 val; 474a9e9dd9cSNicolin Chen int ret; 475a9e9dd9cSNicolin Chen 476a9e9dd9cSNicolin Chen ret = of_property_read_u32(child, "reg", &val); 477a9e9dd9cSNicolin Chen if (ret) { 478a9e9dd9cSNicolin Chen dev_err(dev, "missing reg property of %s\n", child->name); 479a9e9dd9cSNicolin Chen return ret; 480a9e9dd9cSNicolin Chen } else if (val > INA3221_CHANNEL3) { 481a9e9dd9cSNicolin Chen dev_err(dev, "invalid reg %d of %s\n", val, child->name); 482a9e9dd9cSNicolin Chen return ret; 483a9e9dd9cSNicolin Chen } 484a9e9dd9cSNicolin Chen 485a9e9dd9cSNicolin Chen input = &ina->inputs[val]; 486a9e9dd9cSNicolin Chen 487a9e9dd9cSNicolin Chen /* Log the disconnected channel input */ 488a9e9dd9cSNicolin Chen if (!of_device_is_available(child)) { 489a9e9dd9cSNicolin Chen input->disconnected = true; 490a9e9dd9cSNicolin Chen return 0; 491a9e9dd9cSNicolin Chen } 492a9e9dd9cSNicolin Chen 493a9e9dd9cSNicolin Chen /* Save the connected input label if available */ 494a9e9dd9cSNicolin Chen of_property_read_string(child, "label", &input->label); 495a9e9dd9cSNicolin Chen 496a9e9dd9cSNicolin Chen /* Overwrite default shunt resistor value optionally */ 497a6e43263SNicolin Chen if (!of_property_read_u32(child, "shunt-resistor-micro-ohms", &val)) { 498a6e43263SNicolin Chen if (val < 1 || val > INT_MAX) { 499a6e43263SNicolin Chen dev_err(dev, "invalid shunt resistor value %u of %s\n", 500a6e43263SNicolin Chen val, child->name); 501a6e43263SNicolin Chen return -EINVAL; 502a6e43263SNicolin Chen } 503a9e9dd9cSNicolin Chen input->shunt_resistor = val; 504a6e43263SNicolin Chen } 505a9e9dd9cSNicolin Chen 506a9e9dd9cSNicolin Chen return 0; 507a9e9dd9cSNicolin Chen } 508a9e9dd9cSNicolin Chen 509a9e9dd9cSNicolin Chen static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina) 510a9e9dd9cSNicolin Chen { 511a9e9dd9cSNicolin Chen const struct device_node *np = dev->of_node; 512a9e9dd9cSNicolin Chen struct device_node *child; 513a9e9dd9cSNicolin Chen int ret; 514a9e9dd9cSNicolin Chen 515a9e9dd9cSNicolin Chen /* Compatible with non-DT platforms */ 516a9e9dd9cSNicolin Chen if (!np) 517a9e9dd9cSNicolin Chen return 0; 518a9e9dd9cSNicolin Chen 519a9e9dd9cSNicolin Chen for_each_child_of_node(np, child) { 520a9e9dd9cSNicolin Chen ret = ina3221_probe_child_from_dt(dev, child, ina); 521a9e9dd9cSNicolin Chen if (ret) 522a9e9dd9cSNicolin Chen return ret; 523a9e9dd9cSNicolin Chen } 524a9e9dd9cSNicolin Chen 525a9e9dd9cSNicolin Chen return 0; 526a9e9dd9cSNicolin Chen } 527a9e9dd9cSNicolin Chen 5287cb6dcffSAndrew F. Davis static int ina3221_probe(struct i2c_client *client, 5297cb6dcffSAndrew F. Davis const struct i2c_device_id *id) 5307cb6dcffSAndrew F. Davis { 5317cb6dcffSAndrew F. Davis struct device *dev = &client->dev; 5327cb6dcffSAndrew F. Davis struct ina3221_data *ina; 5337cb6dcffSAndrew F. Davis struct device *hwmon_dev; 5347cb6dcffSAndrew F. Davis int i, ret; 5357cb6dcffSAndrew F. Davis 5367cb6dcffSAndrew F. Davis ina = devm_kzalloc(dev, sizeof(*ina), GFP_KERNEL); 5377cb6dcffSAndrew F. Davis if (!ina) 5387cb6dcffSAndrew F. Davis return -ENOMEM; 5397cb6dcffSAndrew F. Davis 5407cb6dcffSAndrew F. Davis ina->regmap = devm_regmap_init_i2c(client, &ina3221_regmap_config); 5417cb6dcffSAndrew F. Davis if (IS_ERR(ina->regmap)) { 5427cb6dcffSAndrew F. Davis dev_err(dev, "Unable to allocate register map\n"); 5437cb6dcffSAndrew F. Davis return PTR_ERR(ina->regmap); 5447cb6dcffSAndrew F. Davis } 5457cb6dcffSAndrew F. Davis 5467cb6dcffSAndrew F. Davis for (i = 0; i < F_MAX_FIELDS; i++) { 5477cb6dcffSAndrew F. Davis ina->fields[i] = devm_regmap_field_alloc(dev, 5487cb6dcffSAndrew F. Davis ina->regmap, 5497cb6dcffSAndrew F. Davis ina3221_reg_fields[i]); 5507cb6dcffSAndrew F. Davis if (IS_ERR(ina->fields[i])) { 5517cb6dcffSAndrew F. Davis dev_err(dev, "Unable to allocate regmap fields\n"); 5527cb6dcffSAndrew F. Davis return PTR_ERR(ina->fields[i]); 5537cb6dcffSAndrew F. Davis } 5547cb6dcffSAndrew F. Davis } 5557cb6dcffSAndrew F. Davis 5567cb6dcffSAndrew F. Davis for (i = 0; i < INA3221_NUM_CHANNELS; i++) 557a9e9dd9cSNicolin Chen ina->inputs[i].shunt_resistor = INA3221_RSHUNT_DEFAULT; 558a9e9dd9cSNicolin Chen 559a9e9dd9cSNicolin Chen ret = ina3221_probe_from_dt(dev, ina); 560a9e9dd9cSNicolin Chen if (ret) { 561a9e9dd9cSNicolin Chen dev_err(dev, "Unable to probe from device tree\n"); 562a9e9dd9cSNicolin Chen return ret; 563a9e9dd9cSNicolin Chen } 5647cb6dcffSAndrew F. Davis 5657cb6dcffSAndrew F. Davis ret = regmap_field_write(ina->fields[F_RST], true); 5667cb6dcffSAndrew F. Davis if (ret) { 5677cb6dcffSAndrew F. Davis dev_err(dev, "Unable to reset device\n"); 5687cb6dcffSAndrew F. Davis return ret; 5697cb6dcffSAndrew F. Davis } 5707cb6dcffSAndrew F. Davis 571a9e9dd9cSNicolin Chen /* Sync config register after reset */ 572a9e9dd9cSNicolin Chen ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config); 573a9e9dd9cSNicolin Chen if (ret) 574a9e9dd9cSNicolin Chen return ret; 575a9e9dd9cSNicolin Chen 576a9e9dd9cSNicolin Chen /* Disable channels if their inputs are disconnected */ 577a9e9dd9cSNicolin Chen for (i = 0; i < INA3221_NUM_CHANNELS; i++) { 578a9e9dd9cSNicolin Chen if (ina->inputs[i].disconnected) 579a9e9dd9cSNicolin Chen ina->reg_config &= ~INA3221_CONFIG_CHx_EN(i); 580a9e9dd9cSNicolin Chen } 581a9e9dd9cSNicolin Chen ret = regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config); 582a9e9dd9cSNicolin Chen if (ret) 583a9e9dd9cSNicolin Chen return ret; 584a9e9dd9cSNicolin Chen 58559d608e1SNicolin Chen dev_set_drvdata(dev, ina); 58659d608e1SNicolin Chen 587d4b0166dSNicolin Chen hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, ina, 588d4b0166dSNicolin Chen &ina3221_chip_info, 589d4b0166dSNicolin Chen ina3221_groups); 5907cb6dcffSAndrew F. Davis if (IS_ERR(hwmon_dev)) { 5917cb6dcffSAndrew F. Davis dev_err(dev, "Unable to register hwmon device\n"); 5927cb6dcffSAndrew F. Davis return PTR_ERR(hwmon_dev); 5937cb6dcffSAndrew F. Davis } 5947cb6dcffSAndrew F. Davis 5957cb6dcffSAndrew F. Davis return 0; 5967cb6dcffSAndrew F. Davis } 5977cb6dcffSAndrew F. Davis 598ead21c77SArnd Bergmann static int __maybe_unused ina3221_suspend(struct device *dev) 59959d608e1SNicolin Chen { 60059d608e1SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 60159d608e1SNicolin Chen int ret; 60259d608e1SNicolin Chen 60359d608e1SNicolin Chen /* Save config register value and enable cache-only */ 60459d608e1SNicolin Chen ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config); 60559d608e1SNicolin Chen if (ret) 60659d608e1SNicolin Chen return ret; 60759d608e1SNicolin Chen 60859d608e1SNicolin Chen /* Set to power-down mode for power saving */ 60959d608e1SNicolin Chen ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, 61059d608e1SNicolin Chen INA3221_CONFIG_MODE_MASK, 61159d608e1SNicolin Chen INA3221_CONFIG_MODE_POWERDOWN); 61259d608e1SNicolin Chen if (ret) 61359d608e1SNicolin Chen return ret; 61459d608e1SNicolin Chen 61559d608e1SNicolin Chen regcache_cache_only(ina->regmap, true); 61659d608e1SNicolin Chen regcache_mark_dirty(ina->regmap); 61759d608e1SNicolin Chen 61859d608e1SNicolin Chen return 0; 61959d608e1SNicolin Chen } 62059d608e1SNicolin Chen 621ead21c77SArnd Bergmann static int __maybe_unused ina3221_resume(struct device *dev) 62259d608e1SNicolin Chen { 62359d608e1SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 62459d608e1SNicolin Chen int ret; 62559d608e1SNicolin Chen 62659d608e1SNicolin Chen regcache_cache_only(ina->regmap, false); 62759d608e1SNicolin Chen 62859d608e1SNicolin Chen /* Software reset the chip */ 62959d608e1SNicolin Chen ret = regmap_field_write(ina->fields[F_RST], true); 63059d608e1SNicolin Chen if (ret) { 63159d608e1SNicolin Chen dev_err(dev, "Unable to reset device\n"); 63259d608e1SNicolin Chen return ret; 63359d608e1SNicolin Chen } 63459d608e1SNicolin Chen 63559d608e1SNicolin Chen /* Restore cached register values to hardware */ 63659d608e1SNicolin Chen ret = regcache_sync(ina->regmap); 63759d608e1SNicolin Chen if (ret) 63859d608e1SNicolin Chen return ret; 63959d608e1SNicolin Chen 64059d608e1SNicolin Chen /* Restore config register value to hardware */ 64159d608e1SNicolin Chen ret = regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config); 64259d608e1SNicolin Chen if (ret) 64359d608e1SNicolin Chen return ret; 64459d608e1SNicolin Chen 64559d608e1SNicolin Chen return 0; 64659d608e1SNicolin Chen } 64759d608e1SNicolin Chen 64859d608e1SNicolin Chen static const struct dev_pm_ops ina3221_pm = { 64959d608e1SNicolin Chen SET_SYSTEM_SLEEP_PM_OPS(ina3221_suspend, ina3221_resume) 65059d608e1SNicolin Chen }; 65159d608e1SNicolin Chen 6527cb6dcffSAndrew F. Davis static const struct of_device_id ina3221_of_match_table[] = { 6537cb6dcffSAndrew F. Davis { .compatible = "ti,ina3221", }, 6547cb6dcffSAndrew F. Davis { /* sentinel */ } 6557cb6dcffSAndrew F. Davis }; 6567cb6dcffSAndrew F. Davis MODULE_DEVICE_TABLE(of, ina3221_of_match_table); 6577cb6dcffSAndrew F. Davis 6587cb6dcffSAndrew F. Davis static const struct i2c_device_id ina3221_ids[] = { 6597cb6dcffSAndrew F. Davis { "ina3221", 0 }, 6607cb6dcffSAndrew F. Davis { /* sentinel */ } 6617cb6dcffSAndrew F. Davis }; 6627cb6dcffSAndrew F. Davis MODULE_DEVICE_TABLE(i2c, ina3221_ids); 6637cb6dcffSAndrew F. Davis 6647cb6dcffSAndrew F. Davis static struct i2c_driver ina3221_i2c_driver = { 6657cb6dcffSAndrew F. Davis .probe = ina3221_probe, 6667cb6dcffSAndrew F. Davis .driver = { 6677cb6dcffSAndrew F. Davis .name = INA3221_DRIVER_NAME, 6687cb6dcffSAndrew F. Davis .of_match_table = ina3221_of_match_table, 66959d608e1SNicolin Chen .pm = &ina3221_pm, 6707cb6dcffSAndrew F. Davis }, 6717cb6dcffSAndrew F. Davis .id_table = ina3221_ids, 6727cb6dcffSAndrew F. Davis }; 6737cb6dcffSAndrew F. Davis module_i2c_driver(ina3221_i2c_driver); 6747cb6dcffSAndrew F. Davis 6757cb6dcffSAndrew F. Davis MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); 6767cb6dcffSAndrew F. Davis MODULE_DESCRIPTION("Texas Instruments INA3221 HWMon Driver"); 6777cb6dcffSAndrew F. Davis MODULE_LICENSE("GPL v2"); 678