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> 2187625b24SNicolin Chen #include <linux/mutex.h> 227cb6dcffSAndrew F. Davis #include <linux/of.h> 23323aeb0eSNicolin Chen #include <linux/pm_runtime.h> 247cb6dcffSAndrew F. Davis #include <linux/regmap.h> 255c090abfSNicolin Chen #include <linux/util_macros.h> 267cb6dcffSAndrew F. Davis 277cb6dcffSAndrew F. Davis #define INA3221_DRIVER_NAME "ina3221" 287cb6dcffSAndrew F. Davis 297cb6dcffSAndrew F. Davis #define INA3221_CONFIG 0x00 307cb6dcffSAndrew F. Davis #define INA3221_SHUNT1 0x01 317cb6dcffSAndrew F. Davis #define INA3221_BUS1 0x02 327cb6dcffSAndrew F. Davis #define INA3221_SHUNT2 0x03 337cb6dcffSAndrew F. Davis #define INA3221_BUS2 0x04 347cb6dcffSAndrew F. Davis #define INA3221_SHUNT3 0x05 357cb6dcffSAndrew F. Davis #define INA3221_BUS3 0x06 367cb6dcffSAndrew F. Davis #define INA3221_CRIT1 0x07 377cb6dcffSAndrew F. Davis #define INA3221_WARN1 0x08 387cb6dcffSAndrew F. Davis #define INA3221_CRIT2 0x09 397cb6dcffSAndrew F. Davis #define INA3221_WARN2 0x0a 407cb6dcffSAndrew F. Davis #define INA3221_CRIT3 0x0b 417cb6dcffSAndrew F. Davis #define INA3221_WARN3 0x0c 427cb6dcffSAndrew F. Davis #define INA3221_MASK_ENABLE 0x0f 437cb6dcffSAndrew F. Davis 4459d608e1SNicolin Chen #define INA3221_CONFIG_MODE_MASK GENMASK(2, 0) 4559d608e1SNicolin Chen #define INA3221_CONFIG_MODE_POWERDOWN 0 46791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_SHUNT BIT(0) 47791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_BUS BIT(1) 48791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_CONTINUOUS BIT(2) 494c0415a3SNicolin Chen #define INA3221_CONFIG_VSH_CT_SHIFT 3 504c0415a3SNicolin Chen #define INA3221_CONFIG_VSH_CT_MASK GENMASK(5, 3) 514c0415a3SNicolin Chen #define INA3221_CONFIG_VSH_CT(x) (((x) & GENMASK(5, 3)) >> 3) 524c0415a3SNicolin Chen #define INA3221_CONFIG_VBUS_CT_SHIFT 6 534c0415a3SNicolin Chen #define INA3221_CONFIG_VBUS_CT_MASK GENMASK(8, 6) 544c0415a3SNicolin Chen #define INA3221_CONFIG_VBUS_CT(x) (((x) & GENMASK(8, 6)) >> 6) 555c090abfSNicolin Chen #define INA3221_CONFIG_AVG_SHIFT 9 565c090abfSNicolin Chen #define INA3221_CONFIG_AVG_MASK GENMASK(11, 9) 575c090abfSNicolin Chen #define INA3221_CONFIG_AVG(x) (((x) & GENMASK(11, 9)) >> 9) 584c0415a3SNicolin Chen #define INA3221_CONFIG_CHs_EN_MASK GENMASK(14, 12) 59a9e9dd9cSNicolin Chen #define INA3221_CONFIG_CHx_EN(x) BIT(14 - (x)) 607cb6dcffSAndrew F. Davis 61323aeb0eSNicolin Chen #define INA3221_CONFIG_DEFAULT 0x7127 627cb6dcffSAndrew F. Davis #define INA3221_RSHUNT_DEFAULT 10000 637cb6dcffSAndrew F. Davis 647cb6dcffSAndrew F. Davis enum ina3221_fields { 657cb6dcffSAndrew F. Davis /* Configuration */ 667cb6dcffSAndrew F. Davis F_RST, 677cb6dcffSAndrew F. Davis 684c0415a3SNicolin Chen /* Status Flags */ 694c0415a3SNicolin Chen F_CVRF, 704c0415a3SNicolin Chen 717cb6dcffSAndrew F. Davis /* Alert Flags */ 727cb6dcffSAndrew F. Davis F_WF3, F_WF2, F_WF1, 737cb6dcffSAndrew F. Davis F_CF3, F_CF2, F_CF1, 747cb6dcffSAndrew F. Davis 757cb6dcffSAndrew F. Davis /* sentinel */ 767cb6dcffSAndrew F. Davis F_MAX_FIELDS 777cb6dcffSAndrew F. Davis }; 787cb6dcffSAndrew F. Davis 797cb6dcffSAndrew F. Davis static const struct reg_field ina3221_reg_fields[] = { 807cb6dcffSAndrew F. Davis [F_RST] = REG_FIELD(INA3221_CONFIG, 15, 15), 817cb6dcffSAndrew F. Davis 824c0415a3SNicolin Chen [F_CVRF] = REG_FIELD(INA3221_MASK_ENABLE, 0, 0), 837cb6dcffSAndrew F. Davis [F_WF3] = REG_FIELD(INA3221_MASK_ENABLE, 3, 3), 847cb6dcffSAndrew F. Davis [F_WF2] = REG_FIELD(INA3221_MASK_ENABLE, 4, 4), 857cb6dcffSAndrew F. Davis [F_WF1] = REG_FIELD(INA3221_MASK_ENABLE, 5, 5), 867cb6dcffSAndrew F. Davis [F_CF3] = REG_FIELD(INA3221_MASK_ENABLE, 7, 7), 877cb6dcffSAndrew F. Davis [F_CF2] = REG_FIELD(INA3221_MASK_ENABLE, 8, 8), 887cb6dcffSAndrew F. Davis [F_CF1] = REG_FIELD(INA3221_MASK_ENABLE, 9, 9), 897cb6dcffSAndrew F. Davis }; 907cb6dcffSAndrew F. Davis 917cb6dcffSAndrew F. Davis enum ina3221_channels { 927cb6dcffSAndrew F. Davis INA3221_CHANNEL1, 937cb6dcffSAndrew F. Davis INA3221_CHANNEL2, 947cb6dcffSAndrew F. Davis INA3221_CHANNEL3, 957cb6dcffSAndrew F. Davis INA3221_NUM_CHANNELS 967cb6dcffSAndrew F. Davis }; 977cb6dcffSAndrew F. Davis 987cb6dcffSAndrew F. Davis /** 99a9e9dd9cSNicolin Chen * struct ina3221_input - channel input source specific information 100a9e9dd9cSNicolin Chen * @label: label of channel input source 101a9e9dd9cSNicolin Chen * @shunt_resistor: shunt resistor value of channel input source 102a9e9dd9cSNicolin Chen * @disconnected: connection status of channel input source 103a9e9dd9cSNicolin Chen */ 104a9e9dd9cSNicolin Chen struct ina3221_input { 105a9e9dd9cSNicolin Chen const char *label; 106a9e9dd9cSNicolin Chen int shunt_resistor; 107a9e9dd9cSNicolin Chen bool disconnected; 108a9e9dd9cSNicolin Chen }; 109a9e9dd9cSNicolin Chen 110a9e9dd9cSNicolin Chen /** 1117cb6dcffSAndrew F. Davis * struct ina3221_data - device specific information 112323aeb0eSNicolin Chen * @pm_dev: Device pointer for pm runtime 1137cb6dcffSAndrew F. Davis * @regmap: Register map of the device 1147cb6dcffSAndrew F. Davis * @fields: Register fields of the device 115a9e9dd9cSNicolin Chen * @inputs: Array of channel input source specific structures 11687625b24SNicolin Chen * @lock: mutex lock to serialize sysfs attribute accesses 11759d608e1SNicolin Chen * @reg_config: Register value of INA3221_CONFIG 11843dece16SNicolin Chen * @single_shot: running in single-shot operating mode 1197cb6dcffSAndrew F. Davis */ 1207cb6dcffSAndrew F. Davis struct ina3221_data { 121323aeb0eSNicolin Chen struct device *pm_dev; 1227cb6dcffSAndrew F. Davis struct regmap *regmap; 1237cb6dcffSAndrew F. Davis struct regmap_field *fields[F_MAX_FIELDS]; 124a9e9dd9cSNicolin Chen struct ina3221_input inputs[INA3221_NUM_CHANNELS]; 12587625b24SNicolin Chen struct mutex lock; 12659d608e1SNicolin Chen u32 reg_config; 12743dece16SNicolin Chen 12843dece16SNicolin Chen bool single_shot; 1297cb6dcffSAndrew F. Davis }; 1307cb6dcffSAndrew F. Davis 131a9e9dd9cSNicolin Chen static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel) 132a9e9dd9cSNicolin Chen { 133323aeb0eSNicolin Chen return pm_runtime_active(ina->pm_dev) && 134323aeb0eSNicolin Chen (ina->reg_config & INA3221_CONFIG_CHx_EN(channel)); 135a9e9dd9cSNicolin Chen } 136a9e9dd9cSNicolin Chen 1374c0415a3SNicolin Chen /* Lookup table for Bus and Shunt conversion times in usec */ 1384c0415a3SNicolin Chen static const u16 ina3221_conv_time[] = { 1394c0415a3SNicolin Chen 140, 204, 332, 588, 1100, 2116, 4156, 8244, 1404c0415a3SNicolin Chen }; 1414c0415a3SNicolin Chen 1425c090abfSNicolin Chen /* Lookup table for number of samples using in averaging mode */ 1435c090abfSNicolin Chen static const int ina3221_avg_samples[] = { 1445c090abfSNicolin Chen 1, 4, 16, 64, 128, 256, 512, 1024, 1455c090abfSNicolin Chen }; 1465c090abfSNicolin Chen 1474c0415a3SNicolin Chen static inline int ina3221_wait_for_data(struct ina3221_data *ina) 1484c0415a3SNicolin Chen { 1494c0415a3SNicolin Chen u32 channels = hweight16(ina->reg_config & INA3221_CONFIG_CHs_EN_MASK); 1504c0415a3SNicolin Chen u32 vbus_ct_idx = INA3221_CONFIG_VBUS_CT(ina->reg_config); 1514c0415a3SNicolin Chen u32 vsh_ct_idx = INA3221_CONFIG_VSH_CT(ina->reg_config); 1525c090abfSNicolin Chen u32 samples_idx = INA3221_CONFIG_AVG(ina->reg_config); 1535c090abfSNicolin Chen u32 samples = ina3221_avg_samples[samples_idx]; 1544c0415a3SNicolin Chen u32 vbus_ct = ina3221_conv_time[vbus_ct_idx]; 1554c0415a3SNicolin Chen u32 vsh_ct = ina3221_conv_time[vsh_ct_idx]; 1564c0415a3SNicolin Chen u32 wait, cvrf; 1574c0415a3SNicolin Chen 1584c0415a3SNicolin Chen /* Calculate total conversion time */ 1595c090abfSNicolin Chen wait = channels * (vbus_ct + vsh_ct) * samples; 1604c0415a3SNicolin Chen 1614c0415a3SNicolin Chen /* Polling the CVRF bit to make sure read data is ready */ 1624c0415a3SNicolin Chen return regmap_field_read_poll_timeout(ina->fields[F_CVRF], 1634c0415a3SNicolin Chen cvrf, cvrf, wait, 100000); 1644c0415a3SNicolin Chen } 1654c0415a3SNicolin Chen 1667cb6dcffSAndrew F. Davis static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg, 1677cb6dcffSAndrew F. Davis int *val) 1687cb6dcffSAndrew F. Davis { 1697cb6dcffSAndrew F. Davis unsigned int regval; 1707cb6dcffSAndrew F. Davis int ret; 1717cb6dcffSAndrew F. Davis 1727cb6dcffSAndrew F. Davis ret = regmap_read(ina->regmap, reg, ®val); 1737cb6dcffSAndrew F. Davis if (ret) 1747cb6dcffSAndrew F. Davis return ret; 1757cb6dcffSAndrew F. Davis 1767cb6dcffSAndrew F. Davis *val = sign_extend32(regval >> 3, 12); 1777cb6dcffSAndrew F. Davis 1787cb6dcffSAndrew F. Davis return 0; 1797cb6dcffSAndrew F. Davis } 1807cb6dcffSAndrew F. Davis 181d4b0166dSNicolin Chen static const u8 ina3221_in_reg[] = { 182d4b0166dSNicolin Chen INA3221_BUS1, 183d4b0166dSNicolin Chen INA3221_BUS2, 184d4b0166dSNicolin Chen INA3221_BUS3, 185d4b0166dSNicolin Chen INA3221_SHUNT1, 186d4b0166dSNicolin Chen INA3221_SHUNT2, 187d4b0166dSNicolin Chen INA3221_SHUNT3, 188d4b0166dSNicolin Chen }; 1897cb6dcffSAndrew F. Davis 1905c090abfSNicolin Chen static int ina3221_read_chip(struct device *dev, u32 attr, long *val) 1915c090abfSNicolin Chen { 1925c090abfSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 1935c090abfSNicolin Chen int regval; 1945c090abfSNicolin Chen 1955c090abfSNicolin Chen switch (attr) { 1965c090abfSNicolin Chen case hwmon_chip_samples: 1975c090abfSNicolin Chen regval = INA3221_CONFIG_AVG(ina->reg_config); 1985c090abfSNicolin Chen *val = ina3221_avg_samples[regval]; 1995c090abfSNicolin Chen return 0; 2005c090abfSNicolin Chen default: 2015c090abfSNicolin Chen return -EOPNOTSUPP; 2025c090abfSNicolin Chen } 2035c090abfSNicolin Chen } 2045c090abfSNicolin Chen 205d4b0166dSNicolin Chen static int ina3221_read_in(struct device *dev, u32 attr, int channel, long *val) 206d4b0166dSNicolin Chen { 207d4b0166dSNicolin Chen const bool is_shunt = channel > INA3221_CHANNEL3; 208d4b0166dSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 209d4b0166dSNicolin Chen u8 reg = ina3221_in_reg[channel]; 210d4b0166dSNicolin Chen int regval, ret; 211d4b0166dSNicolin Chen 212d4b0166dSNicolin Chen /* Translate shunt channel index to sensor channel index */ 213d4b0166dSNicolin Chen channel %= INA3221_NUM_CHANNELS; 214d4b0166dSNicolin Chen 215d4b0166dSNicolin Chen switch (attr) { 216d4b0166dSNicolin Chen case hwmon_in_input: 217d4b0166dSNicolin Chen if (!ina3221_is_enabled(ina, channel)) 218a9e9dd9cSNicolin Chen return -ENODATA; 219a9e9dd9cSNicolin Chen 22043dece16SNicolin Chen /* Write CONFIG register to trigger a single-shot measurement */ 22143dece16SNicolin Chen if (ina->single_shot) 22243dece16SNicolin Chen regmap_write(ina->regmap, INA3221_CONFIG, 22343dece16SNicolin Chen ina->reg_config); 22443dece16SNicolin Chen 2254c0415a3SNicolin Chen ret = ina3221_wait_for_data(ina); 2264c0415a3SNicolin Chen if (ret) 2274c0415a3SNicolin Chen return ret; 2284c0415a3SNicolin Chen 229d4b0166dSNicolin Chen ret = ina3221_read_value(ina, reg, ®val); 2307cb6dcffSAndrew F. Davis if (ret) 2317cb6dcffSAndrew F. Davis return ret; 2327cb6dcffSAndrew F. Davis 233d4b0166dSNicolin Chen /* 234d4b0166dSNicolin Chen * Scale of shunt voltage (uV): LSB is 40uV 235d4b0166dSNicolin Chen * Scale of bus voltage (mV): LSB is 8mV 236d4b0166dSNicolin Chen */ 237d4b0166dSNicolin Chen *val = regval * (is_shunt ? 40 : 8); 238d4b0166dSNicolin Chen return 0; 239d4b0166dSNicolin Chen case hwmon_in_enable: 240d4b0166dSNicolin Chen *val = ina3221_is_enabled(ina, channel); 241d4b0166dSNicolin Chen return 0; 242d4b0166dSNicolin Chen default: 243d4b0166dSNicolin Chen return -EOPNOTSUPP; 244d4b0166dSNicolin Chen } 2457cb6dcffSAndrew F. Davis } 2467cb6dcffSAndrew F. Davis 247d4b0166dSNicolin Chen static const u8 ina3221_curr_reg[][INA3221_NUM_CHANNELS] = { 248d4b0166dSNicolin Chen [hwmon_curr_input] = { INA3221_SHUNT1, INA3221_SHUNT2, INA3221_SHUNT3 }, 249d4b0166dSNicolin Chen [hwmon_curr_max] = { INA3221_WARN1, INA3221_WARN2, INA3221_WARN3 }, 250d4b0166dSNicolin Chen [hwmon_curr_crit] = { INA3221_CRIT1, INA3221_CRIT2, INA3221_CRIT3 }, 251d4b0166dSNicolin Chen [hwmon_curr_max_alarm] = { F_WF1, F_WF2, F_WF3 }, 252d4b0166dSNicolin Chen [hwmon_curr_crit_alarm] = { F_CF1, F_CF2, F_CF3 }, 253d4b0166dSNicolin Chen }; 254d4b0166dSNicolin Chen 255d4b0166dSNicolin Chen static int ina3221_read_curr(struct device *dev, u32 attr, 256d4b0166dSNicolin Chen int channel, long *val) 2577cb6dcffSAndrew F. Davis { 2587cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 259a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 260a9e9dd9cSNicolin Chen int resistance_uo = input->shunt_resistor; 261d4b0166dSNicolin Chen u8 reg = ina3221_curr_reg[attr][channel]; 262d4b0166dSNicolin Chen int regval, voltage_nv, ret; 2637cb6dcffSAndrew F. Davis 264d4b0166dSNicolin Chen switch (attr) { 265d4b0166dSNicolin Chen case hwmon_curr_input: 266d4b0166dSNicolin Chen if (!ina3221_is_enabled(ina, channel)) 267a9e9dd9cSNicolin Chen return -ENODATA; 2684c0415a3SNicolin Chen 26943dece16SNicolin Chen /* Write CONFIG register to trigger a single-shot measurement */ 27043dece16SNicolin Chen if (ina->single_shot) 27143dece16SNicolin Chen regmap_write(ina->regmap, INA3221_CONFIG, 27243dece16SNicolin Chen ina->reg_config); 27343dece16SNicolin Chen 2744c0415a3SNicolin Chen ret = ina3221_wait_for_data(ina); 2754c0415a3SNicolin Chen if (ret) 2764c0415a3SNicolin Chen return ret; 2774c0415a3SNicolin Chen 278d4b0166dSNicolin Chen /* fall through */ 279d4b0166dSNicolin Chen case hwmon_curr_crit: 280d4b0166dSNicolin Chen case hwmon_curr_max: 281d4b0166dSNicolin Chen ret = ina3221_read_value(ina, reg, ®val); 2827cb6dcffSAndrew F. Davis if (ret) 2837cb6dcffSAndrew F. Davis return ret; 2847cb6dcffSAndrew F. Davis 285d4b0166dSNicolin Chen /* Scale of shunt voltage: LSB is 40uV (40000nV) */ 286d4b0166dSNicolin Chen voltage_nv = regval * 40000; 287d4b0166dSNicolin Chen /* Return current in mA */ 288d4b0166dSNicolin Chen *val = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo); 289d4b0166dSNicolin Chen return 0; 290d4b0166dSNicolin Chen case hwmon_curr_crit_alarm: 291d4b0166dSNicolin Chen case hwmon_curr_max_alarm: 292efb0489eSNicolin Chen /* No actual register read if channel is disabled */ 293efb0489eSNicolin Chen if (!ina3221_is_enabled(ina, channel)) { 294efb0489eSNicolin Chen /* Return 0 for alert flags */ 295efb0489eSNicolin Chen *val = 0; 296efb0489eSNicolin Chen return 0; 297efb0489eSNicolin Chen } 298d4b0166dSNicolin Chen ret = regmap_field_read(ina->fields[reg], ®val); 299d4b0166dSNicolin Chen if (ret) 300d4b0166dSNicolin Chen return ret; 301d4b0166dSNicolin Chen *val = regval; 302d4b0166dSNicolin Chen return 0; 303d4b0166dSNicolin Chen default: 304d4b0166dSNicolin Chen return -EOPNOTSUPP; 305d4b0166dSNicolin Chen } 3067cb6dcffSAndrew F. Davis } 3077cb6dcffSAndrew F. Davis 3085c090abfSNicolin Chen static int ina3221_write_chip(struct device *dev, u32 attr, long val) 3095c090abfSNicolin Chen { 3105c090abfSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 3115c090abfSNicolin Chen int ret, idx; 312521c0b61SNicolin Chen u32 tmp; 3135c090abfSNicolin Chen 3145c090abfSNicolin Chen switch (attr) { 3155c090abfSNicolin Chen case hwmon_chip_samples: 3165c090abfSNicolin Chen idx = find_closest(val, ina3221_avg_samples, 3175c090abfSNicolin Chen ARRAY_SIZE(ina3221_avg_samples)); 3185c090abfSNicolin Chen 319521c0b61SNicolin Chen tmp = (ina->reg_config & ~INA3221_CONFIG_AVG_MASK) | 320521c0b61SNicolin Chen (idx << INA3221_CONFIG_AVG_SHIFT); 321521c0b61SNicolin Chen ret = regmap_write(ina->regmap, INA3221_CONFIG, tmp); 3225c090abfSNicolin Chen if (ret) 3235c090abfSNicolin Chen return ret; 3245c090abfSNicolin Chen 3255c090abfSNicolin Chen /* Update reg_config accordingly */ 326521c0b61SNicolin Chen ina->reg_config = tmp; 327521c0b61SNicolin Chen return 0; 3285c090abfSNicolin Chen default: 3295c090abfSNicolin Chen return -EOPNOTSUPP; 3305c090abfSNicolin Chen } 3315c090abfSNicolin Chen } 3325c090abfSNicolin Chen 333d4b0166dSNicolin Chen static int ina3221_write_curr(struct device *dev, u32 attr, 334d4b0166dSNicolin Chen int channel, long val) 3357cb6dcffSAndrew F. Davis { 3367cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 337a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 338a9e9dd9cSNicolin Chen int resistance_uo = input->shunt_resistor; 339d4b0166dSNicolin Chen u8 reg = ina3221_curr_reg[attr][channel]; 340d4b0166dSNicolin Chen int regval, current_ma, voltage_uv; 3417cb6dcffSAndrew F. Davis 3427cb6dcffSAndrew F. Davis /* clamp current */ 343d4b0166dSNicolin Chen current_ma = clamp_val(val, 3447cb6dcffSAndrew F. Davis INT_MIN / resistance_uo, 3457cb6dcffSAndrew F. Davis INT_MAX / resistance_uo); 3467cb6dcffSAndrew F. Davis 3477cb6dcffSAndrew F. Davis voltage_uv = DIV_ROUND_CLOSEST(current_ma * resistance_uo, 1000); 3487cb6dcffSAndrew F. Davis 3497cb6dcffSAndrew F. Davis /* clamp voltage */ 3507cb6dcffSAndrew F. Davis voltage_uv = clamp_val(voltage_uv, -163800, 163800); 3517cb6dcffSAndrew F. Davis 3527cb6dcffSAndrew F. Davis /* 1 / 40uV(scale) << 3(register shift) = 5 */ 353d4b0166dSNicolin Chen regval = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8; 3547cb6dcffSAndrew F. Davis 355d4b0166dSNicolin Chen return regmap_write(ina->regmap, reg, regval); 356d4b0166dSNicolin Chen } 357d4b0166dSNicolin Chen 358d4b0166dSNicolin Chen static int ina3221_write_enable(struct device *dev, int channel, bool enable) 359d4b0166dSNicolin Chen { 360d4b0166dSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 361d4b0166dSNicolin Chen u16 config, mask = INA3221_CONFIG_CHx_EN(channel); 362323aeb0eSNicolin Chen u16 config_old = ina->reg_config & mask; 363521c0b61SNicolin Chen u32 tmp; 364d4b0166dSNicolin Chen int ret; 365d4b0166dSNicolin Chen 366d4b0166dSNicolin Chen config = enable ? mask : 0; 367d4b0166dSNicolin Chen 368323aeb0eSNicolin Chen /* Bypass if enable status is not being changed */ 369323aeb0eSNicolin Chen if (config_old == config) 370323aeb0eSNicolin Chen return 0; 371323aeb0eSNicolin Chen 372323aeb0eSNicolin Chen /* For enabling routine, increase refcount and resume() at first */ 373323aeb0eSNicolin Chen if (enable) { 374323aeb0eSNicolin Chen ret = pm_runtime_get_sync(ina->pm_dev); 375323aeb0eSNicolin Chen if (ret < 0) { 376323aeb0eSNicolin Chen dev_err(dev, "Failed to get PM runtime\n"); 377323aeb0eSNicolin Chen return ret; 378323aeb0eSNicolin Chen } 379323aeb0eSNicolin Chen } 380323aeb0eSNicolin Chen 381d4b0166dSNicolin Chen /* Enable or disable the channel */ 382521c0b61SNicolin Chen tmp = (ina->reg_config & ~mask) | (config & mask); 383521c0b61SNicolin Chen ret = regmap_write(ina->regmap, INA3221_CONFIG, tmp); 3847cb6dcffSAndrew F. Davis if (ret) 385323aeb0eSNicolin Chen goto fail; 3867cb6dcffSAndrew F. Davis 387d4b0166dSNicolin Chen /* Cache the latest config register value */ 388521c0b61SNicolin Chen ina->reg_config = tmp; 389323aeb0eSNicolin Chen 390323aeb0eSNicolin Chen /* For disabling routine, decrease refcount or suspend() at last */ 391323aeb0eSNicolin Chen if (!enable) 392323aeb0eSNicolin Chen pm_runtime_put_sync(ina->pm_dev); 393d4b0166dSNicolin Chen 394d4b0166dSNicolin Chen return 0; 395323aeb0eSNicolin Chen 396323aeb0eSNicolin Chen fail: 397323aeb0eSNicolin Chen if (enable) { 398323aeb0eSNicolin Chen dev_err(dev, "Failed to enable channel %d: error %d\n", 399323aeb0eSNicolin Chen channel, ret); 400323aeb0eSNicolin Chen pm_runtime_put_sync(ina->pm_dev); 401323aeb0eSNicolin Chen } 402323aeb0eSNicolin Chen 403323aeb0eSNicolin Chen return ret; 4047cb6dcffSAndrew F. Davis } 4057cb6dcffSAndrew F. Davis 406d4b0166dSNicolin Chen static int ina3221_read(struct device *dev, enum hwmon_sensor_types type, 407d4b0166dSNicolin Chen u32 attr, int channel, long *val) 408d4b0166dSNicolin Chen { 40987625b24SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 41087625b24SNicolin Chen int ret; 41187625b24SNicolin Chen 41287625b24SNicolin Chen mutex_lock(&ina->lock); 41387625b24SNicolin Chen 414d4b0166dSNicolin Chen switch (type) { 4155c090abfSNicolin Chen case hwmon_chip: 4165c090abfSNicolin Chen ret = ina3221_read_chip(dev, attr, val); 4175c090abfSNicolin Chen break; 418d4b0166dSNicolin Chen case hwmon_in: 419d4b0166dSNicolin Chen /* 0-align channel ID */ 42087625b24SNicolin Chen ret = ina3221_read_in(dev, attr, channel - 1, val); 42187625b24SNicolin Chen break; 422d4b0166dSNicolin Chen case hwmon_curr: 42387625b24SNicolin Chen ret = ina3221_read_curr(dev, attr, channel, val); 42487625b24SNicolin Chen break; 425d4b0166dSNicolin Chen default: 42687625b24SNicolin Chen ret = -EOPNOTSUPP; 42787625b24SNicolin Chen break; 428d4b0166dSNicolin Chen } 42987625b24SNicolin Chen 43087625b24SNicolin Chen mutex_unlock(&ina->lock); 43187625b24SNicolin Chen 43287625b24SNicolin Chen return ret; 433d4b0166dSNicolin Chen } 434d4b0166dSNicolin Chen 435d4b0166dSNicolin Chen static int ina3221_write(struct device *dev, enum hwmon_sensor_types type, 436d4b0166dSNicolin Chen u32 attr, int channel, long val) 437d4b0166dSNicolin Chen { 43887625b24SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 43987625b24SNicolin Chen int ret; 44087625b24SNicolin Chen 44187625b24SNicolin Chen mutex_lock(&ina->lock); 44287625b24SNicolin Chen 443d4b0166dSNicolin Chen switch (type) { 4445c090abfSNicolin Chen case hwmon_chip: 4455c090abfSNicolin Chen ret = ina3221_write_chip(dev, attr, val); 4465c090abfSNicolin Chen break; 447d4b0166dSNicolin Chen case hwmon_in: 448d4b0166dSNicolin Chen /* 0-align channel ID */ 44987625b24SNicolin Chen ret = ina3221_write_enable(dev, channel - 1, val); 45087625b24SNicolin Chen break; 451d4b0166dSNicolin Chen case hwmon_curr: 45287625b24SNicolin Chen ret = ina3221_write_curr(dev, attr, channel, val); 45387625b24SNicolin Chen break; 454d4b0166dSNicolin Chen default: 45587625b24SNicolin Chen ret = -EOPNOTSUPP; 45687625b24SNicolin Chen break; 457d4b0166dSNicolin Chen } 45887625b24SNicolin Chen 45987625b24SNicolin Chen mutex_unlock(&ina->lock); 46087625b24SNicolin Chen 46187625b24SNicolin Chen return ret; 462d4b0166dSNicolin Chen } 463d4b0166dSNicolin Chen 464d4b0166dSNicolin Chen static int ina3221_read_string(struct device *dev, enum hwmon_sensor_types type, 465d4b0166dSNicolin Chen u32 attr, int channel, const char **str) 466d4b0166dSNicolin Chen { 467d4b0166dSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 468d4b0166dSNicolin Chen int index = channel - 1; 469d4b0166dSNicolin Chen 470d4b0166dSNicolin Chen *str = ina->inputs[index].label; 471d4b0166dSNicolin Chen 472d4b0166dSNicolin Chen return 0; 473d4b0166dSNicolin Chen } 474d4b0166dSNicolin Chen 475d4b0166dSNicolin Chen static umode_t ina3221_is_visible(const void *drvdata, 476d4b0166dSNicolin Chen enum hwmon_sensor_types type, 477d4b0166dSNicolin Chen u32 attr, int channel) 478d4b0166dSNicolin Chen { 479d4b0166dSNicolin Chen const struct ina3221_data *ina = drvdata; 480d4b0166dSNicolin Chen const struct ina3221_input *input = NULL; 481d4b0166dSNicolin Chen 482d4b0166dSNicolin Chen switch (type) { 4835c090abfSNicolin Chen case hwmon_chip: 4845c090abfSNicolin Chen switch (attr) { 4855c090abfSNicolin Chen case hwmon_chip_samples: 4865c090abfSNicolin Chen return 0644; 4875c090abfSNicolin Chen default: 4885c090abfSNicolin Chen return 0; 4895c090abfSNicolin Chen } 490d4b0166dSNicolin Chen case hwmon_in: 491d4b0166dSNicolin Chen /* Ignore in0_ */ 492d4b0166dSNicolin Chen if (channel == 0) 493d4b0166dSNicolin Chen return 0; 494d4b0166dSNicolin Chen 495d4b0166dSNicolin Chen switch (attr) { 496d4b0166dSNicolin Chen case hwmon_in_label: 497d4b0166dSNicolin Chen if (channel - 1 <= INA3221_CHANNEL3) 498d4b0166dSNicolin Chen input = &ina->inputs[channel - 1]; 499d4b0166dSNicolin Chen /* Hide label node if label is not provided */ 500d4b0166dSNicolin Chen return (input && input->label) ? 0444 : 0; 501d4b0166dSNicolin Chen case hwmon_in_input: 502d4b0166dSNicolin Chen return 0444; 503d4b0166dSNicolin Chen case hwmon_in_enable: 504d4b0166dSNicolin Chen return 0644; 505d4b0166dSNicolin Chen default: 506d4b0166dSNicolin Chen return 0; 507d4b0166dSNicolin Chen } 508d4b0166dSNicolin Chen case hwmon_curr: 509d4b0166dSNicolin Chen switch (attr) { 510d4b0166dSNicolin Chen case hwmon_curr_input: 511d4b0166dSNicolin Chen case hwmon_curr_crit_alarm: 512d4b0166dSNicolin Chen case hwmon_curr_max_alarm: 513d4b0166dSNicolin Chen return 0444; 514d4b0166dSNicolin Chen case hwmon_curr_crit: 515d4b0166dSNicolin Chen case hwmon_curr_max: 516d4b0166dSNicolin Chen return 0644; 517d4b0166dSNicolin Chen default: 518d4b0166dSNicolin Chen return 0; 519d4b0166dSNicolin Chen } 520d4b0166dSNicolin Chen default: 521d4b0166dSNicolin Chen return 0; 522d4b0166dSNicolin Chen } 523d4b0166dSNicolin Chen } 524d4b0166dSNicolin Chen 5256f307b7cSGuenter Roeck #define INA3221_HWMON_CURR_CONFIG (HWMON_C_INPUT | \ 5266f307b7cSGuenter Roeck HWMON_C_CRIT | HWMON_C_CRIT_ALARM | \ 5276f307b7cSGuenter Roeck HWMON_C_MAX | HWMON_C_MAX_ALARM) 5286f307b7cSGuenter Roeck 5296f307b7cSGuenter Roeck static const struct hwmon_channel_info *ina3221_info[] = { 5305c090abfSNicolin Chen HWMON_CHANNEL_INFO(chip, 5315c090abfSNicolin Chen HWMON_C_SAMPLES), 5326f307b7cSGuenter Roeck HWMON_CHANNEL_INFO(in, 533d4b0166dSNicolin Chen /* 0: dummy, skipped in is_visible */ 534d4b0166dSNicolin Chen HWMON_I_INPUT, 535d4b0166dSNicolin Chen /* 1-3: input voltage Channels */ 536d4b0166dSNicolin Chen HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, 537d4b0166dSNicolin Chen HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, 538d4b0166dSNicolin Chen HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, 539d4b0166dSNicolin Chen /* 4-6: shunt voltage Channels */ 540d4b0166dSNicolin Chen HWMON_I_INPUT, 541d4b0166dSNicolin Chen HWMON_I_INPUT, 5426f307b7cSGuenter Roeck HWMON_I_INPUT), 5436f307b7cSGuenter Roeck HWMON_CHANNEL_INFO(curr, 544d4b0166dSNicolin Chen INA3221_HWMON_CURR_CONFIG, 545d4b0166dSNicolin Chen INA3221_HWMON_CURR_CONFIG, 5466f307b7cSGuenter Roeck INA3221_HWMON_CURR_CONFIG), 547d4b0166dSNicolin Chen NULL 548d4b0166dSNicolin Chen }; 549d4b0166dSNicolin Chen 550d4b0166dSNicolin Chen static const struct hwmon_ops ina3221_hwmon_ops = { 551d4b0166dSNicolin Chen .is_visible = ina3221_is_visible, 552d4b0166dSNicolin Chen .read_string = ina3221_read_string, 553d4b0166dSNicolin Chen .read = ina3221_read, 554d4b0166dSNicolin Chen .write = ina3221_write, 555d4b0166dSNicolin Chen }; 556d4b0166dSNicolin Chen 557d4b0166dSNicolin Chen static const struct hwmon_chip_info ina3221_chip_info = { 558d4b0166dSNicolin Chen .ops = &ina3221_hwmon_ops, 559d4b0166dSNicolin Chen .info = ina3221_info, 560d4b0166dSNicolin Chen }; 561d4b0166dSNicolin Chen 562d4b0166dSNicolin Chen /* Extra attribute groups */ 563a4ec92edSGuenter Roeck static ssize_t ina3221_shunt_show(struct device *dev, 5647cb6dcffSAndrew F. Davis struct device_attribute *attr, char *buf) 5657cb6dcffSAndrew F. Davis { 5667cb6dcffSAndrew F. Davis struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); 5677cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 5687cb6dcffSAndrew F. Davis unsigned int channel = sd_attr->index; 569a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 5707cb6dcffSAndrew F. Davis 571a9e9dd9cSNicolin Chen return snprintf(buf, PAGE_SIZE, "%d\n", input->shunt_resistor); 5727cb6dcffSAndrew F. Davis } 5737cb6dcffSAndrew F. Davis 574a4ec92edSGuenter Roeck static ssize_t ina3221_shunt_store(struct device *dev, 5757cb6dcffSAndrew F. Davis struct device_attribute *attr, 5767cb6dcffSAndrew F. Davis const char *buf, size_t count) 5777cb6dcffSAndrew F. Davis { 5787cb6dcffSAndrew F. Davis struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); 5797cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 5807cb6dcffSAndrew F. Davis unsigned int channel = sd_attr->index; 581a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 5829ad0df1aSGuenter Roeck int val; 5837cb6dcffSAndrew F. Davis int ret; 5847cb6dcffSAndrew F. Davis 5859ad0df1aSGuenter Roeck ret = kstrtoint(buf, 0, &val); 5867cb6dcffSAndrew F. Davis if (ret) 5877cb6dcffSAndrew F. Davis return ret; 5887cb6dcffSAndrew F. Davis 5899ad0df1aSGuenter Roeck val = clamp_val(val, 1, INT_MAX); 5907cb6dcffSAndrew F. Davis 591a9e9dd9cSNicolin Chen input->shunt_resistor = val; 5927cb6dcffSAndrew F. Davis 5937cb6dcffSAndrew F. Davis return count; 5947cb6dcffSAndrew F. Davis } 5957cb6dcffSAndrew F. Davis 5967cb6dcffSAndrew F. Davis /* shunt resistance */ 597a4ec92edSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(shunt1_resistor, ina3221_shunt, INA3221_CHANNEL1); 598a4ec92edSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(shunt2_resistor, ina3221_shunt, INA3221_CHANNEL2); 599a4ec92edSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(shunt3_resistor, ina3221_shunt, INA3221_CHANNEL3); 6007cb6dcffSAndrew F. Davis 6017cb6dcffSAndrew F. Davis static struct attribute *ina3221_attrs[] = { 6027cb6dcffSAndrew F. Davis &sensor_dev_attr_shunt1_resistor.dev_attr.attr, 6037cb6dcffSAndrew F. Davis &sensor_dev_attr_shunt2_resistor.dev_attr.attr, 6047cb6dcffSAndrew F. Davis &sensor_dev_attr_shunt3_resistor.dev_attr.attr, 6057cb6dcffSAndrew F. Davis NULL, 6067cb6dcffSAndrew F. Davis }; 607d4b0166dSNicolin Chen ATTRIBUTE_GROUPS(ina3221); 6087cb6dcffSAndrew F. Davis 6097cb6dcffSAndrew F. Davis static const struct regmap_range ina3221_yes_ranges[] = { 610c20217b3SNicolin Chen regmap_reg_range(INA3221_CONFIG, INA3221_BUS3), 6117cb6dcffSAndrew F. Davis regmap_reg_range(INA3221_MASK_ENABLE, INA3221_MASK_ENABLE), 6127cb6dcffSAndrew F. Davis }; 6137cb6dcffSAndrew F. Davis 6147cb6dcffSAndrew F. Davis static const struct regmap_access_table ina3221_volatile_table = { 6157cb6dcffSAndrew F. Davis .yes_ranges = ina3221_yes_ranges, 6167cb6dcffSAndrew F. Davis .n_yes_ranges = ARRAY_SIZE(ina3221_yes_ranges), 6177cb6dcffSAndrew F. Davis }; 6187cb6dcffSAndrew F. Davis 6197cb6dcffSAndrew F. Davis static const struct regmap_config ina3221_regmap_config = { 6207cb6dcffSAndrew F. Davis .reg_bits = 8, 6217cb6dcffSAndrew F. Davis .val_bits = 16, 6227cb6dcffSAndrew F. Davis 6237cb6dcffSAndrew F. Davis .cache_type = REGCACHE_RBTREE, 6247cb6dcffSAndrew F. Davis .volatile_table = &ina3221_volatile_table, 6257cb6dcffSAndrew F. Davis }; 6267cb6dcffSAndrew F. Davis 627a9e9dd9cSNicolin Chen static int ina3221_probe_child_from_dt(struct device *dev, 628a9e9dd9cSNicolin Chen struct device_node *child, 629a9e9dd9cSNicolin Chen struct ina3221_data *ina) 630a9e9dd9cSNicolin Chen { 631a9e9dd9cSNicolin Chen struct ina3221_input *input; 632a9e9dd9cSNicolin Chen u32 val; 633a9e9dd9cSNicolin Chen int ret; 634a9e9dd9cSNicolin Chen 635a9e9dd9cSNicolin Chen ret = of_property_read_u32(child, "reg", &val); 636a9e9dd9cSNicolin Chen if (ret) { 6371b1f4efaSRob Herring dev_err(dev, "missing reg property of %pOFn\n", child); 638a9e9dd9cSNicolin Chen return ret; 639a9e9dd9cSNicolin Chen } else if (val > INA3221_CHANNEL3) { 6401b1f4efaSRob Herring dev_err(dev, "invalid reg %d of %pOFn\n", val, child); 641a9e9dd9cSNicolin Chen return ret; 642a9e9dd9cSNicolin Chen } 643a9e9dd9cSNicolin Chen 644a9e9dd9cSNicolin Chen input = &ina->inputs[val]; 645a9e9dd9cSNicolin Chen 646a9e9dd9cSNicolin Chen /* Log the disconnected channel input */ 647a9e9dd9cSNicolin Chen if (!of_device_is_available(child)) { 648a9e9dd9cSNicolin Chen input->disconnected = true; 649a9e9dd9cSNicolin Chen return 0; 650a9e9dd9cSNicolin Chen } 651a9e9dd9cSNicolin Chen 652a9e9dd9cSNicolin Chen /* Save the connected input label if available */ 653a9e9dd9cSNicolin Chen of_property_read_string(child, "label", &input->label); 654a9e9dd9cSNicolin Chen 655a9e9dd9cSNicolin Chen /* Overwrite default shunt resistor value optionally */ 656a6e43263SNicolin Chen if (!of_property_read_u32(child, "shunt-resistor-micro-ohms", &val)) { 657a6e43263SNicolin Chen if (val < 1 || val > INT_MAX) { 6581b1f4efaSRob Herring dev_err(dev, "invalid shunt resistor value %u of %pOFn\n", 6591b1f4efaSRob Herring val, child); 660a6e43263SNicolin Chen return -EINVAL; 661a6e43263SNicolin Chen } 662a9e9dd9cSNicolin Chen input->shunt_resistor = val; 663a6e43263SNicolin Chen } 664a9e9dd9cSNicolin Chen 665a9e9dd9cSNicolin Chen return 0; 666a9e9dd9cSNicolin Chen } 667a9e9dd9cSNicolin Chen 668a9e9dd9cSNicolin Chen static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina) 669a9e9dd9cSNicolin Chen { 670a9e9dd9cSNicolin Chen const struct device_node *np = dev->of_node; 671a9e9dd9cSNicolin Chen struct device_node *child; 672a9e9dd9cSNicolin Chen int ret; 673a9e9dd9cSNicolin Chen 674a9e9dd9cSNicolin Chen /* Compatible with non-DT platforms */ 675a9e9dd9cSNicolin Chen if (!np) 676a9e9dd9cSNicolin Chen return 0; 677a9e9dd9cSNicolin Chen 67843dece16SNicolin Chen ina->single_shot = of_property_read_bool(np, "ti,single-shot"); 67943dece16SNicolin Chen 680a9e9dd9cSNicolin Chen for_each_child_of_node(np, child) { 681a9e9dd9cSNicolin Chen ret = ina3221_probe_child_from_dt(dev, child, ina); 682a9e9dd9cSNicolin Chen if (ret) 683a9e9dd9cSNicolin Chen return ret; 684a9e9dd9cSNicolin Chen } 685a9e9dd9cSNicolin Chen 686a9e9dd9cSNicolin Chen return 0; 687a9e9dd9cSNicolin Chen } 688a9e9dd9cSNicolin Chen 6897cb6dcffSAndrew F. Davis static int ina3221_probe(struct i2c_client *client, 6907cb6dcffSAndrew F. Davis const struct i2c_device_id *id) 6917cb6dcffSAndrew F. Davis { 6927cb6dcffSAndrew F. Davis struct device *dev = &client->dev; 6937cb6dcffSAndrew F. Davis struct ina3221_data *ina; 6947cb6dcffSAndrew F. Davis struct device *hwmon_dev; 6957cb6dcffSAndrew F. Davis int i, ret; 6967cb6dcffSAndrew F. Davis 6977cb6dcffSAndrew F. Davis ina = devm_kzalloc(dev, sizeof(*ina), GFP_KERNEL); 6987cb6dcffSAndrew F. Davis if (!ina) 6997cb6dcffSAndrew F. Davis return -ENOMEM; 7007cb6dcffSAndrew F. Davis 7017cb6dcffSAndrew F. Davis ina->regmap = devm_regmap_init_i2c(client, &ina3221_regmap_config); 7027cb6dcffSAndrew F. Davis if (IS_ERR(ina->regmap)) { 7037cb6dcffSAndrew F. Davis dev_err(dev, "Unable to allocate register map\n"); 7047cb6dcffSAndrew F. Davis return PTR_ERR(ina->regmap); 7057cb6dcffSAndrew F. Davis } 7067cb6dcffSAndrew F. Davis 7077cb6dcffSAndrew F. Davis for (i = 0; i < F_MAX_FIELDS; i++) { 7087cb6dcffSAndrew F. Davis ina->fields[i] = devm_regmap_field_alloc(dev, 7097cb6dcffSAndrew F. Davis ina->regmap, 7107cb6dcffSAndrew F. Davis ina3221_reg_fields[i]); 7117cb6dcffSAndrew F. Davis if (IS_ERR(ina->fields[i])) { 7127cb6dcffSAndrew F. Davis dev_err(dev, "Unable to allocate regmap fields\n"); 7137cb6dcffSAndrew F. Davis return PTR_ERR(ina->fields[i]); 7147cb6dcffSAndrew F. Davis } 7157cb6dcffSAndrew F. Davis } 7167cb6dcffSAndrew F. Davis 7177cb6dcffSAndrew F. Davis for (i = 0; i < INA3221_NUM_CHANNELS; i++) 718a9e9dd9cSNicolin Chen ina->inputs[i].shunt_resistor = INA3221_RSHUNT_DEFAULT; 719a9e9dd9cSNicolin Chen 720a9e9dd9cSNicolin Chen ret = ina3221_probe_from_dt(dev, ina); 721a9e9dd9cSNicolin Chen if (ret) { 722a9e9dd9cSNicolin Chen dev_err(dev, "Unable to probe from device tree\n"); 723a9e9dd9cSNicolin Chen return ret; 724a9e9dd9cSNicolin Chen } 7257cb6dcffSAndrew F. Davis 726323aeb0eSNicolin Chen /* The driver will be reset, so use reset value */ 727323aeb0eSNicolin Chen ina->reg_config = INA3221_CONFIG_DEFAULT; 728a9e9dd9cSNicolin Chen 72943dece16SNicolin Chen /* Clear continuous bit to use single-shot mode */ 73043dece16SNicolin Chen if (ina->single_shot) 73143dece16SNicolin Chen ina->reg_config &= ~INA3221_CONFIG_MODE_CONTINUOUS; 73243dece16SNicolin Chen 733a9e9dd9cSNicolin Chen /* Disable channels if their inputs are disconnected */ 734a9e9dd9cSNicolin Chen for (i = 0; i < INA3221_NUM_CHANNELS; i++) { 735a9e9dd9cSNicolin Chen if (ina->inputs[i].disconnected) 736a9e9dd9cSNicolin Chen ina->reg_config &= ~INA3221_CONFIG_CHx_EN(i); 737a9e9dd9cSNicolin Chen } 738a9e9dd9cSNicolin Chen 739323aeb0eSNicolin Chen ina->pm_dev = dev; 74087625b24SNicolin Chen mutex_init(&ina->lock); 74159d608e1SNicolin Chen dev_set_drvdata(dev, ina); 74259d608e1SNicolin Chen 743323aeb0eSNicolin Chen /* Enable PM runtime -- status is suspended by default */ 744323aeb0eSNicolin Chen pm_runtime_enable(ina->pm_dev); 745323aeb0eSNicolin Chen 746323aeb0eSNicolin Chen /* Initialize (resume) the device */ 747323aeb0eSNicolin Chen for (i = 0; i < INA3221_NUM_CHANNELS; i++) { 748323aeb0eSNicolin Chen if (ina->inputs[i].disconnected) 749323aeb0eSNicolin Chen continue; 750323aeb0eSNicolin Chen /* Match the refcount with number of enabled channels */ 751323aeb0eSNicolin Chen ret = pm_runtime_get_sync(ina->pm_dev); 752323aeb0eSNicolin Chen if (ret < 0) 753323aeb0eSNicolin Chen goto fail; 754323aeb0eSNicolin Chen } 755323aeb0eSNicolin Chen 756d4b0166dSNicolin Chen hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, ina, 757d4b0166dSNicolin Chen &ina3221_chip_info, 758d4b0166dSNicolin Chen ina3221_groups); 7597cb6dcffSAndrew F. Davis if (IS_ERR(hwmon_dev)) { 7607cb6dcffSAndrew F. Davis dev_err(dev, "Unable to register hwmon device\n"); 761323aeb0eSNicolin Chen ret = PTR_ERR(hwmon_dev); 762323aeb0eSNicolin Chen goto fail; 7637cb6dcffSAndrew F. Davis } 7647cb6dcffSAndrew F. Davis 7657cb6dcffSAndrew F. Davis return 0; 766323aeb0eSNicolin Chen 767323aeb0eSNicolin Chen fail: 768323aeb0eSNicolin Chen pm_runtime_disable(ina->pm_dev); 769323aeb0eSNicolin Chen pm_runtime_set_suspended(ina->pm_dev); 770323aeb0eSNicolin Chen /* pm_runtime_put_noidle() will decrease the PM refcount until 0 */ 771323aeb0eSNicolin Chen for (i = 0; i < INA3221_NUM_CHANNELS; i++) 772323aeb0eSNicolin Chen pm_runtime_put_noidle(ina->pm_dev); 773323aeb0eSNicolin Chen mutex_destroy(&ina->lock); 774323aeb0eSNicolin Chen 775323aeb0eSNicolin Chen return ret; 7767cb6dcffSAndrew F. Davis } 7777cb6dcffSAndrew F. Davis 77887625b24SNicolin Chen static int ina3221_remove(struct i2c_client *client) 77987625b24SNicolin Chen { 78087625b24SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(&client->dev); 781323aeb0eSNicolin Chen int i; 782323aeb0eSNicolin Chen 783323aeb0eSNicolin Chen pm_runtime_disable(ina->pm_dev); 784323aeb0eSNicolin Chen pm_runtime_set_suspended(ina->pm_dev); 785323aeb0eSNicolin Chen 786323aeb0eSNicolin Chen /* pm_runtime_put_noidle() will decrease the PM refcount until 0 */ 787323aeb0eSNicolin Chen for (i = 0; i < INA3221_NUM_CHANNELS; i++) 788323aeb0eSNicolin Chen pm_runtime_put_noidle(ina->pm_dev); 78987625b24SNicolin Chen 79087625b24SNicolin Chen mutex_destroy(&ina->lock); 79187625b24SNicolin Chen 79287625b24SNicolin Chen return 0; 79387625b24SNicolin Chen } 79487625b24SNicolin Chen 795ead21c77SArnd Bergmann static int __maybe_unused ina3221_suspend(struct device *dev) 79659d608e1SNicolin Chen { 79759d608e1SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 79859d608e1SNicolin Chen int ret; 79959d608e1SNicolin Chen 80059d608e1SNicolin Chen /* Save config register value and enable cache-only */ 80159d608e1SNicolin Chen ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config); 80259d608e1SNicolin Chen if (ret) 80359d608e1SNicolin Chen return ret; 80459d608e1SNicolin Chen 80559d608e1SNicolin Chen /* Set to power-down mode for power saving */ 80659d608e1SNicolin Chen ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, 80759d608e1SNicolin Chen INA3221_CONFIG_MODE_MASK, 80859d608e1SNicolin Chen INA3221_CONFIG_MODE_POWERDOWN); 80959d608e1SNicolin Chen if (ret) 81059d608e1SNicolin Chen return ret; 81159d608e1SNicolin Chen 81259d608e1SNicolin Chen regcache_cache_only(ina->regmap, true); 81359d608e1SNicolin Chen regcache_mark_dirty(ina->regmap); 81459d608e1SNicolin Chen 81559d608e1SNicolin Chen return 0; 81659d608e1SNicolin Chen } 81759d608e1SNicolin Chen 818ead21c77SArnd Bergmann static int __maybe_unused ina3221_resume(struct device *dev) 81959d608e1SNicolin Chen { 82059d608e1SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 82159d608e1SNicolin Chen int ret; 82259d608e1SNicolin Chen 82359d608e1SNicolin Chen regcache_cache_only(ina->regmap, false); 82459d608e1SNicolin Chen 82559d608e1SNicolin Chen /* Software reset the chip */ 82659d608e1SNicolin Chen ret = regmap_field_write(ina->fields[F_RST], true); 82759d608e1SNicolin Chen if (ret) { 82859d608e1SNicolin Chen dev_err(dev, "Unable to reset device\n"); 82959d608e1SNicolin Chen return ret; 83059d608e1SNicolin Chen } 83159d608e1SNicolin Chen 83259d608e1SNicolin Chen /* Restore cached register values to hardware */ 83359d608e1SNicolin Chen ret = regcache_sync(ina->regmap); 83459d608e1SNicolin Chen if (ret) 83559d608e1SNicolin Chen return ret; 83659d608e1SNicolin Chen 83759d608e1SNicolin Chen /* Restore config register value to hardware */ 83859d608e1SNicolin Chen ret = regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config); 83959d608e1SNicolin Chen if (ret) 84059d608e1SNicolin Chen return ret; 84159d608e1SNicolin Chen 84259d608e1SNicolin Chen return 0; 84359d608e1SNicolin Chen } 84459d608e1SNicolin Chen 84559d608e1SNicolin Chen static const struct dev_pm_ops ina3221_pm = { 846323aeb0eSNicolin Chen SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 847323aeb0eSNicolin Chen pm_runtime_force_resume) 848323aeb0eSNicolin Chen SET_RUNTIME_PM_OPS(ina3221_suspend, ina3221_resume, NULL) 84959d608e1SNicolin Chen }; 85059d608e1SNicolin Chen 8517cb6dcffSAndrew F. Davis static const struct of_device_id ina3221_of_match_table[] = { 8527cb6dcffSAndrew F. Davis { .compatible = "ti,ina3221", }, 8537cb6dcffSAndrew F. Davis { /* sentinel */ } 8547cb6dcffSAndrew F. Davis }; 8557cb6dcffSAndrew F. Davis MODULE_DEVICE_TABLE(of, ina3221_of_match_table); 8567cb6dcffSAndrew F. Davis 8577cb6dcffSAndrew F. Davis static const struct i2c_device_id ina3221_ids[] = { 8587cb6dcffSAndrew F. Davis { "ina3221", 0 }, 8597cb6dcffSAndrew F. Davis { /* sentinel */ } 8607cb6dcffSAndrew F. Davis }; 8617cb6dcffSAndrew F. Davis MODULE_DEVICE_TABLE(i2c, ina3221_ids); 8627cb6dcffSAndrew F. Davis 8637cb6dcffSAndrew F. Davis static struct i2c_driver ina3221_i2c_driver = { 8647cb6dcffSAndrew F. Davis .probe = ina3221_probe, 86587625b24SNicolin Chen .remove = ina3221_remove, 8667cb6dcffSAndrew F. Davis .driver = { 8677cb6dcffSAndrew F. Davis .name = INA3221_DRIVER_NAME, 8687cb6dcffSAndrew F. Davis .of_match_table = ina3221_of_match_table, 86959d608e1SNicolin Chen .pm = &ina3221_pm, 8707cb6dcffSAndrew F. Davis }, 8717cb6dcffSAndrew F. Davis .id_table = ina3221_ids, 8727cb6dcffSAndrew F. Davis }; 8737cb6dcffSAndrew F. Davis module_i2c_driver(ina3221_i2c_driver); 8747cb6dcffSAndrew F. Davis 8757cb6dcffSAndrew F. Davis MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); 8767cb6dcffSAndrew F. Davis MODULE_DESCRIPTION("Texas Instruments INA3221 HWMon Driver"); 8777cb6dcffSAndrew F. Davis MODULE_LICENSE("GPL v2"); 878