11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 27cb6dcffSAndrew F. Davis /* 37cb6dcffSAndrew F. Davis * INA3221 Triple Current/Voltage Monitor 47cb6dcffSAndrew F. Davis * 57cb6dcffSAndrew F. Davis * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/ 67cb6dcffSAndrew F. Davis * Andrew F. Davis <afd@ti.com> 77cb6dcffSAndrew F. Davis */ 87cb6dcffSAndrew F. Davis 97cb6dcffSAndrew F. Davis #include <linux/hwmon.h> 107cb6dcffSAndrew F. Davis #include <linux/hwmon-sysfs.h> 117cb6dcffSAndrew F. Davis #include <linux/i2c.h> 127cb6dcffSAndrew F. Davis #include <linux/module.h> 1387625b24SNicolin Chen #include <linux/mutex.h> 147cb6dcffSAndrew F. Davis #include <linux/of.h> 15323aeb0eSNicolin Chen #include <linux/pm_runtime.h> 167cb6dcffSAndrew F. Davis #include <linux/regmap.h> 175c090abfSNicolin Chen #include <linux/util_macros.h> 187cb6dcffSAndrew F. Davis 197cb6dcffSAndrew F. Davis #define INA3221_DRIVER_NAME "ina3221" 207cb6dcffSAndrew F. Davis 217cb6dcffSAndrew F. Davis #define INA3221_CONFIG 0x00 227cb6dcffSAndrew F. Davis #define INA3221_SHUNT1 0x01 237cb6dcffSAndrew F. Davis #define INA3221_BUS1 0x02 247cb6dcffSAndrew F. Davis #define INA3221_SHUNT2 0x03 257cb6dcffSAndrew F. Davis #define INA3221_BUS2 0x04 267cb6dcffSAndrew F. Davis #define INA3221_SHUNT3 0x05 277cb6dcffSAndrew F. Davis #define INA3221_BUS3 0x06 287cb6dcffSAndrew F. Davis #define INA3221_CRIT1 0x07 297cb6dcffSAndrew F. Davis #define INA3221_WARN1 0x08 307cb6dcffSAndrew F. Davis #define INA3221_CRIT2 0x09 317cb6dcffSAndrew F. Davis #define INA3221_WARN2 0x0a 327cb6dcffSAndrew F. Davis #define INA3221_CRIT3 0x0b 337cb6dcffSAndrew F. Davis #define INA3221_WARN3 0x0c 347cb6dcffSAndrew F. Davis #define INA3221_MASK_ENABLE 0x0f 357cb6dcffSAndrew F. Davis 3659d608e1SNicolin Chen #define INA3221_CONFIG_MODE_MASK GENMASK(2, 0) 3759d608e1SNicolin Chen #define INA3221_CONFIG_MODE_POWERDOWN 0 38791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_SHUNT BIT(0) 39791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_BUS BIT(1) 40791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_CONTINUOUS BIT(2) 414c0415a3SNicolin Chen #define INA3221_CONFIG_VSH_CT_SHIFT 3 424c0415a3SNicolin Chen #define INA3221_CONFIG_VSH_CT_MASK GENMASK(5, 3) 434c0415a3SNicolin Chen #define INA3221_CONFIG_VSH_CT(x) (((x) & GENMASK(5, 3)) >> 3) 444c0415a3SNicolin Chen #define INA3221_CONFIG_VBUS_CT_SHIFT 6 454c0415a3SNicolin Chen #define INA3221_CONFIG_VBUS_CT_MASK GENMASK(8, 6) 464c0415a3SNicolin Chen #define INA3221_CONFIG_VBUS_CT(x) (((x) & GENMASK(8, 6)) >> 6) 475c090abfSNicolin Chen #define INA3221_CONFIG_AVG_SHIFT 9 485c090abfSNicolin Chen #define INA3221_CONFIG_AVG_MASK GENMASK(11, 9) 495c090abfSNicolin Chen #define INA3221_CONFIG_AVG(x) (((x) & GENMASK(11, 9)) >> 9) 504c0415a3SNicolin Chen #define INA3221_CONFIG_CHs_EN_MASK GENMASK(14, 12) 51a9e9dd9cSNicolin Chen #define INA3221_CONFIG_CHx_EN(x) BIT(14 - (x)) 527cb6dcffSAndrew F. Davis 53323aeb0eSNicolin Chen #define INA3221_CONFIG_DEFAULT 0x7127 547cb6dcffSAndrew F. Davis #define INA3221_RSHUNT_DEFAULT 10000 557cb6dcffSAndrew F. Davis 567cb6dcffSAndrew F. Davis enum ina3221_fields { 577cb6dcffSAndrew F. Davis /* Configuration */ 587cb6dcffSAndrew F. Davis F_RST, 597cb6dcffSAndrew F. Davis 604c0415a3SNicolin Chen /* Status Flags */ 614c0415a3SNicolin Chen F_CVRF, 624c0415a3SNicolin Chen 637cb6dcffSAndrew F. Davis /* Alert Flags */ 647cb6dcffSAndrew F. Davis F_WF3, F_WF2, F_WF1, 657cb6dcffSAndrew F. Davis F_CF3, F_CF2, F_CF1, 667cb6dcffSAndrew F. Davis 677cb6dcffSAndrew F. Davis /* sentinel */ 687cb6dcffSAndrew F. Davis F_MAX_FIELDS 697cb6dcffSAndrew F. Davis }; 707cb6dcffSAndrew F. Davis 717cb6dcffSAndrew F. Davis static const struct reg_field ina3221_reg_fields[] = { 727cb6dcffSAndrew F. Davis [F_RST] = REG_FIELD(INA3221_CONFIG, 15, 15), 737cb6dcffSAndrew F. Davis 744c0415a3SNicolin Chen [F_CVRF] = REG_FIELD(INA3221_MASK_ENABLE, 0, 0), 757cb6dcffSAndrew F. Davis [F_WF3] = REG_FIELD(INA3221_MASK_ENABLE, 3, 3), 767cb6dcffSAndrew F. Davis [F_WF2] = REG_FIELD(INA3221_MASK_ENABLE, 4, 4), 777cb6dcffSAndrew F. Davis [F_WF1] = REG_FIELD(INA3221_MASK_ENABLE, 5, 5), 787cb6dcffSAndrew F. Davis [F_CF3] = REG_FIELD(INA3221_MASK_ENABLE, 7, 7), 797cb6dcffSAndrew F. Davis [F_CF2] = REG_FIELD(INA3221_MASK_ENABLE, 8, 8), 807cb6dcffSAndrew F. Davis [F_CF1] = REG_FIELD(INA3221_MASK_ENABLE, 9, 9), 817cb6dcffSAndrew F. Davis }; 827cb6dcffSAndrew F. Davis 837cb6dcffSAndrew F. Davis enum ina3221_channels { 847cb6dcffSAndrew F. Davis INA3221_CHANNEL1, 857cb6dcffSAndrew F. Davis INA3221_CHANNEL2, 867cb6dcffSAndrew F. Davis INA3221_CHANNEL3, 877cb6dcffSAndrew F. Davis INA3221_NUM_CHANNELS 887cb6dcffSAndrew F. Davis }; 897cb6dcffSAndrew F. Davis 907cb6dcffSAndrew F. Davis /** 91a9e9dd9cSNicolin Chen * struct ina3221_input - channel input source specific information 92a9e9dd9cSNicolin Chen * @label: label of channel input source 93a9e9dd9cSNicolin Chen * @shunt_resistor: shunt resistor value of channel input source 94a9e9dd9cSNicolin Chen * @disconnected: connection status of channel input source 95a9e9dd9cSNicolin Chen */ 96a9e9dd9cSNicolin Chen struct ina3221_input { 97a9e9dd9cSNicolin Chen const char *label; 98a9e9dd9cSNicolin Chen int shunt_resistor; 99a9e9dd9cSNicolin Chen bool disconnected; 100a9e9dd9cSNicolin Chen }; 101a9e9dd9cSNicolin Chen 102a9e9dd9cSNicolin Chen /** 1037cb6dcffSAndrew F. Davis * struct ina3221_data - device specific information 104323aeb0eSNicolin Chen * @pm_dev: Device pointer for pm runtime 1057cb6dcffSAndrew F. Davis * @regmap: Register map of the device 1067cb6dcffSAndrew F. Davis * @fields: Register fields of the device 107a9e9dd9cSNicolin Chen * @inputs: Array of channel input source specific structures 10887625b24SNicolin Chen * @lock: mutex lock to serialize sysfs attribute accesses 10959d608e1SNicolin Chen * @reg_config: Register value of INA3221_CONFIG 11043dece16SNicolin Chen * @single_shot: running in single-shot operating mode 1117cb6dcffSAndrew F. Davis */ 1127cb6dcffSAndrew F. Davis struct ina3221_data { 113323aeb0eSNicolin Chen struct device *pm_dev; 1147cb6dcffSAndrew F. Davis struct regmap *regmap; 1157cb6dcffSAndrew F. Davis struct regmap_field *fields[F_MAX_FIELDS]; 116a9e9dd9cSNicolin Chen struct ina3221_input inputs[INA3221_NUM_CHANNELS]; 11787625b24SNicolin Chen struct mutex lock; 11859d608e1SNicolin Chen u32 reg_config; 11943dece16SNicolin Chen 12043dece16SNicolin Chen bool single_shot; 1217cb6dcffSAndrew F. Davis }; 1227cb6dcffSAndrew F. Davis 123a9e9dd9cSNicolin Chen static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel) 124a9e9dd9cSNicolin Chen { 125323aeb0eSNicolin Chen return pm_runtime_active(ina->pm_dev) && 126323aeb0eSNicolin Chen (ina->reg_config & INA3221_CONFIG_CHx_EN(channel)); 127a9e9dd9cSNicolin Chen } 128a9e9dd9cSNicolin Chen 1294c0415a3SNicolin Chen /* Lookup table for Bus and Shunt conversion times in usec */ 1304c0415a3SNicolin Chen static const u16 ina3221_conv_time[] = { 1314c0415a3SNicolin Chen 140, 204, 332, 588, 1100, 2116, 4156, 8244, 1324c0415a3SNicolin Chen }; 1334c0415a3SNicolin Chen 1345c090abfSNicolin Chen /* Lookup table for number of samples using in averaging mode */ 1355c090abfSNicolin Chen static const int ina3221_avg_samples[] = { 1365c090abfSNicolin Chen 1, 4, 16, 64, 128, 256, 512, 1024, 1375c090abfSNicolin Chen }; 1385c090abfSNicolin Chen 139023912dbSNicolin Chen /* Converting update_interval in msec to conversion time in usec */ 140023912dbSNicolin Chen static inline u32 ina3221_interval_ms_to_conv_time(u16 config, int interval) 1414c0415a3SNicolin Chen { 142023912dbSNicolin Chen u32 channels = hweight16(config & INA3221_CONFIG_CHs_EN_MASK); 143023912dbSNicolin Chen u32 samples_idx = INA3221_CONFIG_AVG(config); 144023912dbSNicolin Chen u32 samples = ina3221_avg_samples[samples_idx]; 145023912dbSNicolin Chen 146023912dbSNicolin Chen /* Bisect the result to Bus and Shunt conversion times */ 147023912dbSNicolin Chen return DIV_ROUND_CLOSEST(interval * 1000 / 2, channels * samples); 148023912dbSNicolin Chen } 149023912dbSNicolin Chen 150023912dbSNicolin Chen /* Converting CONFIG register value to update_interval in usec */ 151023912dbSNicolin Chen static inline u32 ina3221_reg_to_interval_us(u16 config) 152023912dbSNicolin Chen { 153023912dbSNicolin Chen u32 channels = hweight16(config & INA3221_CONFIG_CHs_EN_MASK); 154023912dbSNicolin Chen u32 vbus_ct_idx = INA3221_CONFIG_VBUS_CT(config); 155023912dbSNicolin Chen u32 vsh_ct_idx = INA3221_CONFIG_VSH_CT(config); 156023912dbSNicolin Chen u32 samples_idx = INA3221_CONFIG_AVG(config); 1575c090abfSNicolin Chen u32 samples = ina3221_avg_samples[samples_idx]; 1584c0415a3SNicolin Chen u32 vbus_ct = ina3221_conv_time[vbus_ct_idx]; 1594c0415a3SNicolin Chen u32 vsh_ct = ina3221_conv_time[vsh_ct_idx]; 1604c0415a3SNicolin Chen 1614c0415a3SNicolin Chen /* Calculate total conversion time */ 162023912dbSNicolin Chen return channels * (vbus_ct + vsh_ct) * samples; 163023912dbSNicolin Chen } 164023912dbSNicolin Chen 165023912dbSNicolin Chen static inline int ina3221_wait_for_data(struct ina3221_data *ina) 166023912dbSNicolin Chen { 167023912dbSNicolin Chen u32 wait, cvrf; 168023912dbSNicolin Chen 169023912dbSNicolin Chen wait = ina3221_reg_to_interval_us(ina->reg_config); 1704c0415a3SNicolin Chen 1714c0415a3SNicolin Chen /* Polling the CVRF bit to make sure read data is ready */ 1724c0415a3SNicolin Chen return regmap_field_read_poll_timeout(ina->fields[F_CVRF], 1734c0415a3SNicolin Chen cvrf, cvrf, wait, 100000); 1744c0415a3SNicolin Chen } 1754c0415a3SNicolin Chen 1767cb6dcffSAndrew F. Davis static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg, 1777cb6dcffSAndrew F. Davis int *val) 1787cb6dcffSAndrew F. Davis { 1797cb6dcffSAndrew F. Davis unsigned int regval; 1807cb6dcffSAndrew F. Davis int ret; 1817cb6dcffSAndrew F. Davis 1827cb6dcffSAndrew F. Davis ret = regmap_read(ina->regmap, reg, ®val); 1837cb6dcffSAndrew F. Davis if (ret) 1847cb6dcffSAndrew F. Davis return ret; 1857cb6dcffSAndrew F. Davis 1867cb6dcffSAndrew F. Davis *val = sign_extend32(regval >> 3, 12); 1877cb6dcffSAndrew F. Davis 1887cb6dcffSAndrew F. Davis return 0; 1897cb6dcffSAndrew F. Davis } 1907cb6dcffSAndrew F. Davis 191d4b0166dSNicolin Chen static const u8 ina3221_in_reg[] = { 192d4b0166dSNicolin Chen INA3221_BUS1, 193d4b0166dSNicolin Chen INA3221_BUS2, 194d4b0166dSNicolin Chen INA3221_BUS3, 195d4b0166dSNicolin Chen INA3221_SHUNT1, 196d4b0166dSNicolin Chen INA3221_SHUNT2, 197d4b0166dSNicolin Chen INA3221_SHUNT3, 198d4b0166dSNicolin Chen }; 1997cb6dcffSAndrew F. Davis 2005c090abfSNicolin Chen static int ina3221_read_chip(struct device *dev, u32 attr, long *val) 2015c090abfSNicolin Chen { 2025c090abfSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 2035c090abfSNicolin Chen int regval; 2045c090abfSNicolin Chen 2055c090abfSNicolin Chen switch (attr) { 2065c090abfSNicolin Chen case hwmon_chip_samples: 2075c090abfSNicolin Chen regval = INA3221_CONFIG_AVG(ina->reg_config); 2085c090abfSNicolin Chen *val = ina3221_avg_samples[regval]; 2095c090abfSNicolin Chen return 0; 210023912dbSNicolin Chen case hwmon_chip_update_interval: 211023912dbSNicolin Chen /* Return in msec */ 212023912dbSNicolin Chen *val = ina3221_reg_to_interval_us(ina->reg_config); 213023912dbSNicolin Chen *val = DIV_ROUND_CLOSEST(*val, 1000); 214023912dbSNicolin Chen return 0; 2155c090abfSNicolin Chen default: 2165c090abfSNicolin Chen return -EOPNOTSUPP; 2175c090abfSNicolin Chen } 2185c090abfSNicolin Chen } 2195c090abfSNicolin Chen 220d4b0166dSNicolin Chen static int ina3221_read_in(struct device *dev, u32 attr, int channel, long *val) 221d4b0166dSNicolin Chen { 222d4b0166dSNicolin Chen const bool is_shunt = channel > INA3221_CHANNEL3; 223d4b0166dSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 224d4b0166dSNicolin Chen u8 reg = ina3221_in_reg[channel]; 225d4b0166dSNicolin Chen int regval, ret; 226d4b0166dSNicolin Chen 227d4b0166dSNicolin Chen /* Translate shunt channel index to sensor channel index */ 228d4b0166dSNicolin Chen channel %= INA3221_NUM_CHANNELS; 229d4b0166dSNicolin Chen 230d4b0166dSNicolin Chen switch (attr) { 231d4b0166dSNicolin Chen case hwmon_in_input: 232d4b0166dSNicolin Chen if (!ina3221_is_enabled(ina, channel)) 233a9e9dd9cSNicolin Chen return -ENODATA; 234a9e9dd9cSNicolin Chen 23543dece16SNicolin Chen /* Write CONFIG register to trigger a single-shot measurement */ 23643dece16SNicolin Chen if (ina->single_shot) 23743dece16SNicolin Chen regmap_write(ina->regmap, INA3221_CONFIG, 23843dece16SNicolin Chen ina->reg_config); 23943dece16SNicolin Chen 2404c0415a3SNicolin Chen ret = ina3221_wait_for_data(ina); 2414c0415a3SNicolin Chen if (ret) 2424c0415a3SNicolin Chen return ret; 2434c0415a3SNicolin Chen 244d4b0166dSNicolin Chen ret = ina3221_read_value(ina, reg, ®val); 2457cb6dcffSAndrew F. Davis if (ret) 2467cb6dcffSAndrew F. Davis return ret; 2477cb6dcffSAndrew F. Davis 248d4b0166dSNicolin Chen /* 249d4b0166dSNicolin Chen * Scale of shunt voltage (uV): LSB is 40uV 250d4b0166dSNicolin Chen * Scale of bus voltage (mV): LSB is 8mV 251d4b0166dSNicolin Chen */ 252d4b0166dSNicolin Chen *val = regval * (is_shunt ? 40 : 8); 253d4b0166dSNicolin Chen return 0; 254d4b0166dSNicolin Chen case hwmon_in_enable: 255d4b0166dSNicolin Chen *val = ina3221_is_enabled(ina, channel); 256d4b0166dSNicolin Chen return 0; 257d4b0166dSNicolin Chen default: 258d4b0166dSNicolin Chen return -EOPNOTSUPP; 259d4b0166dSNicolin Chen } 2607cb6dcffSAndrew F. Davis } 2617cb6dcffSAndrew F. Davis 262d4b0166dSNicolin Chen static const u8 ina3221_curr_reg[][INA3221_NUM_CHANNELS] = { 263d4b0166dSNicolin Chen [hwmon_curr_input] = { INA3221_SHUNT1, INA3221_SHUNT2, INA3221_SHUNT3 }, 264d4b0166dSNicolin Chen [hwmon_curr_max] = { INA3221_WARN1, INA3221_WARN2, INA3221_WARN3 }, 265d4b0166dSNicolin Chen [hwmon_curr_crit] = { INA3221_CRIT1, INA3221_CRIT2, INA3221_CRIT3 }, 266d4b0166dSNicolin Chen [hwmon_curr_max_alarm] = { F_WF1, F_WF2, F_WF3 }, 267d4b0166dSNicolin Chen [hwmon_curr_crit_alarm] = { F_CF1, F_CF2, F_CF3 }, 268d4b0166dSNicolin Chen }; 269d4b0166dSNicolin Chen 270d4b0166dSNicolin Chen static int ina3221_read_curr(struct device *dev, u32 attr, 271d4b0166dSNicolin Chen int channel, long *val) 2727cb6dcffSAndrew F. Davis { 2737cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 274a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 275a9e9dd9cSNicolin Chen int resistance_uo = input->shunt_resistor; 276d4b0166dSNicolin Chen u8 reg = ina3221_curr_reg[attr][channel]; 277d4b0166dSNicolin Chen int regval, voltage_nv, ret; 2787cb6dcffSAndrew F. Davis 279d4b0166dSNicolin Chen switch (attr) { 280d4b0166dSNicolin Chen case hwmon_curr_input: 281d4b0166dSNicolin Chen if (!ina3221_is_enabled(ina, channel)) 282a9e9dd9cSNicolin Chen return -ENODATA; 2834c0415a3SNicolin Chen 28443dece16SNicolin Chen /* Write CONFIG register to trigger a single-shot measurement */ 28543dece16SNicolin Chen if (ina->single_shot) 28643dece16SNicolin Chen regmap_write(ina->regmap, INA3221_CONFIG, 28743dece16SNicolin Chen ina->reg_config); 28843dece16SNicolin Chen 2894c0415a3SNicolin Chen ret = ina3221_wait_for_data(ina); 2904c0415a3SNicolin Chen if (ret) 2914c0415a3SNicolin Chen return ret; 2924c0415a3SNicolin Chen 293d4b0166dSNicolin Chen /* fall through */ 294d4b0166dSNicolin Chen case hwmon_curr_crit: 295d4b0166dSNicolin Chen case hwmon_curr_max: 296d4b0166dSNicolin Chen ret = ina3221_read_value(ina, reg, ®val); 2977cb6dcffSAndrew F. Davis if (ret) 2987cb6dcffSAndrew F. Davis return ret; 2997cb6dcffSAndrew F. Davis 300d4b0166dSNicolin Chen /* Scale of shunt voltage: LSB is 40uV (40000nV) */ 301d4b0166dSNicolin Chen voltage_nv = regval * 40000; 302d4b0166dSNicolin Chen /* Return current in mA */ 303d4b0166dSNicolin Chen *val = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo); 304d4b0166dSNicolin Chen return 0; 305d4b0166dSNicolin Chen case hwmon_curr_crit_alarm: 306d4b0166dSNicolin Chen case hwmon_curr_max_alarm: 307efb0489eSNicolin Chen /* No actual register read if channel is disabled */ 308efb0489eSNicolin Chen if (!ina3221_is_enabled(ina, channel)) { 309efb0489eSNicolin Chen /* Return 0 for alert flags */ 310efb0489eSNicolin Chen *val = 0; 311efb0489eSNicolin Chen return 0; 312efb0489eSNicolin Chen } 313d4b0166dSNicolin Chen ret = regmap_field_read(ina->fields[reg], ®val); 314d4b0166dSNicolin Chen if (ret) 315d4b0166dSNicolin Chen return ret; 316d4b0166dSNicolin Chen *val = regval; 317d4b0166dSNicolin Chen return 0; 318d4b0166dSNicolin Chen default: 319d4b0166dSNicolin Chen return -EOPNOTSUPP; 320d4b0166dSNicolin Chen } 3217cb6dcffSAndrew F. Davis } 3227cb6dcffSAndrew F. Davis 3235c090abfSNicolin Chen static int ina3221_write_chip(struct device *dev, u32 attr, long val) 3245c090abfSNicolin Chen { 3255c090abfSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 3265c090abfSNicolin Chen int ret, idx; 327521c0b61SNicolin Chen u32 tmp; 3285c090abfSNicolin Chen 3295c090abfSNicolin Chen switch (attr) { 3305c090abfSNicolin Chen case hwmon_chip_samples: 3315c090abfSNicolin Chen idx = find_closest(val, ina3221_avg_samples, 3325c090abfSNicolin Chen ARRAY_SIZE(ina3221_avg_samples)); 3335c090abfSNicolin Chen 334521c0b61SNicolin Chen tmp = (ina->reg_config & ~INA3221_CONFIG_AVG_MASK) | 335521c0b61SNicolin Chen (idx << INA3221_CONFIG_AVG_SHIFT); 336521c0b61SNicolin Chen ret = regmap_write(ina->regmap, INA3221_CONFIG, tmp); 3375c090abfSNicolin Chen if (ret) 3385c090abfSNicolin Chen return ret; 3395c090abfSNicolin Chen 3405c090abfSNicolin Chen /* Update reg_config accordingly */ 341521c0b61SNicolin Chen ina->reg_config = tmp; 342521c0b61SNicolin Chen return 0; 343023912dbSNicolin Chen case hwmon_chip_update_interval: 344023912dbSNicolin Chen tmp = ina3221_interval_ms_to_conv_time(ina->reg_config, val); 345023912dbSNicolin Chen idx = find_closest(tmp, ina3221_conv_time, 346023912dbSNicolin Chen ARRAY_SIZE(ina3221_conv_time)); 347023912dbSNicolin Chen 348023912dbSNicolin Chen /* Update Bus and Shunt voltage conversion times */ 349023912dbSNicolin Chen tmp = INA3221_CONFIG_VBUS_CT_MASK | INA3221_CONFIG_VSH_CT_MASK; 350023912dbSNicolin Chen tmp = (ina->reg_config & ~tmp) | 351023912dbSNicolin Chen (idx << INA3221_CONFIG_VBUS_CT_SHIFT) | 352023912dbSNicolin Chen (idx << INA3221_CONFIG_VSH_CT_SHIFT); 353023912dbSNicolin Chen ret = regmap_write(ina->regmap, INA3221_CONFIG, tmp); 354023912dbSNicolin Chen if (ret) 355023912dbSNicolin Chen return ret; 356023912dbSNicolin Chen 357023912dbSNicolin Chen /* Update reg_config accordingly */ 358023912dbSNicolin Chen ina->reg_config = tmp; 359023912dbSNicolin Chen return 0; 3605c090abfSNicolin Chen default: 3615c090abfSNicolin Chen return -EOPNOTSUPP; 3625c090abfSNicolin Chen } 3635c090abfSNicolin Chen } 3645c090abfSNicolin Chen 365d4b0166dSNicolin Chen static int ina3221_write_curr(struct device *dev, u32 attr, 366d4b0166dSNicolin Chen int channel, long val) 3677cb6dcffSAndrew F. Davis { 3687cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 369a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 370a9e9dd9cSNicolin Chen int resistance_uo = input->shunt_resistor; 371d4b0166dSNicolin Chen u8 reg = ina3221_curr_reg[attr][channel]; 372d4b0166dSNicolin Chen int regval, current_ma, voltage_uv; 3737cb6dcffSAndrew F. Davis 3747cb6dcffSAndrew F. Davis /* clamp current */ 375d4b0166dSNicolin Chen current_ma = clamp_val(val, 3767cb6dcffSAndrew F. Davis INT_MIN / resistance_uo, 3777cb6dcffSAndrew F. Davis INT_MAX / resistance_uo); 3787cb6dcffSAndrew F. Davis 3797cb6dcffSAndrew F. Davis voltage_uv = DIV_ROUND_CLOSEST(current_ma * resistance_uo, 1000); 3807cb6dcffSAndrew F. Davis 3817cb6dcffSAndrew F. Davis /* clamp voltage */ 3827cb6dcffSAndrew F. Davis voltage_uv = clamp_val(voltage_uv, -163800, 163800); 3837cb6dcffSAndrew F. Davis 3847cb6dcffSAndrew F. Davis /* 1 / 40uV(scale) << 3(register shift) = 5 */ 385d4b0166dSNicolin Chen regval = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8; 3867cb6dcffSAndrew F. Davis 387d4b0166dSNicolin Chen return regmap_write(ina->regmap, reg, regval); 388d4b0166dSNicolin Chen } 389d4b0166dSNicolin Chen 390d4b0166dSNicolin Chen static int ina3221_write_enable(struct device *dev, int channel, bool enable) 391d4b0166dSNicolin Chen { 392d4b0166dSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 393d4b0166dSNicolin Chen u16 config, mask = INA3221_CONFIG_CHx_EN(channel); 394323aeb0eSNicolin Chen u16 config_old = ina->reg_config & mask; 395521c0b61SNicolin Chen u32 tmp; 396d4b0166dSNicolin Chen int ret; 397d4b0166dSNicolin Chen 398d4b0166dSNicolin Chen config = enable ? mask : 0; 399d4b0166dSNicolin Chen 400323aeb0eSNicolin Chen /* Bypass if enable status is not being changed */ 401323aeb0eSNicolin Chen if (config_old == config) 402323aeb0eSNicolin Chen return 0; 403323aeb0eSNicolin Chen 404323aeb0eSNicolin Chen /* For enabling routine, increase refcount and resume() at first */ 405323aeb0eSNicolin Chen if (enable) { 406323aeb0eSNicolin Chen ret = pm_runtime_get_sync(ina->pm_dev); 407323aeb0eSNicolin Chen if (ret < 0) { 408323aeb0eSNicolin Chen dev_err(dev, "Failed to get PM runtime\n"); 409323aeb0eSNicolin Chen return ret; 410323aeb0eSNicolin Chen } 411323aeb0eSNicolin Chen } 412323aeb0eSNicolin Chen 413d4b0166dSNicolin Chen /* Enable or disable the channel */ 414521c0b61SNicolin Chen tmp = (ina->reg_config & ~mask) | (config & mask); 415521c0b61SNicolin Chen ret = regmap_write(ina->regmap, INA3221_CONFIG, tmp); 4167cb6dcffSAndrew F. Davis if (ret) 417323aeb0eSNicolin Chen goto fail; 4187cb6dcffSAndrew F. Davis 419d4b0166dSNicolin Chen /* Cache the latest config register value */ 420521c0b61SNicolin Chen ina->reg_config = tmp; 421323aeb0eSNicolin Chen 422323aeb0eSNicolin Chen /* For disabling routine, decrease refcount or suspend() at last */ 423323aeb0eSNicolin Chen if (!enable) 424323aeb0eSNicolin Chen pm_runtime_put_sync(ina->pm_dev); 425d4b0166dSNicolin Chen 426d4b0166dSNicolin Chen return 0; 427323aeb0eSNicolin Chen 428323aeb0eSNicolin Chen fail: 429323aeb0eSNicolin Chen if (enable) { 430323aeb0eSNicolin Chen dev_err(dev, "Failed to enable channel %d: error %d\n", 431323aeb0eSNicolin Chen channel, ret); 432323aeb0eSNicolin Chen pm_runtime_put_sync(ina->pm_dev); 433323aeb0eSNicolin Chen } 434323aeb0eSNicolin Chen 435323aeb0eSNicolin Chen return ret; 4367cb6dcffSAndrew F. Davis } 4377cb6dcffSAndrew F. Davis 438d4b0166dSNicolin Chen static int ina3221_read(struct device *dev, enum hwmon_sensor_types type, 439d4b0166dSNicolin Chen u32 attr, int channel, long *val) 440d4b0166dSNicolin Chen { 44187625b24SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 44287625b24SNicolin Chen int ret; 44387625b24SNicolin Chen 44487625b24SNicolin Chen mutex_lock(&ina->lock); 44587625b24SNicolin Chen 446d4b0166dSNicolin Chen switch (type) { 4475c090abfSNicolin Chen case hwmon_chip: 4485c090abfSNicolin Chen ret = ina3221_read_chip(dev, attr, val); 4495c090abfSNicolin Chen break; 450d4b0166dSNicolin Chen case hwmon_in: 451d4b0166dSNicolin Chen /* 0-align channel ID */ 45287625b24SNicolin Chen ret = ina3221_read_in(dev, attr, channel - 1, val); 45387625b24SNicolin Chen break; 454d4b0166dSNicolin Chen case hwmon_curr: 45587625b24SNicolin Chen ret = ina3221_read_curr(dev, attr, channel, val); 45687625b24SNicolin Chen break; 457d4b0166dSNicolin Chen default: 45887625b24SNicolin Chen ret = -EOPNOTSUPP; 45987625b24SNicolin Chen break; 460d4b0166dSNicolin Chen } 46187625b24SNicolin Chen 46287625b24SNicolin Chen mutex_unlock(&ina->lock); 46387625b24SNicolin Chen 46487625b24SNicolin Chen return ret; 465d4b0166dSNicolin Chen } 466d4b0166dSNicolin Chen 467d4b0166dSNicolin Chen static int ina3221_write(struct device *dev, enum hwmon_sensor_types type, 468d4b0166dSNicolin Chen u32 attr, int channel, long val) 469d4b0166dSNicolin Chen { 47087625b24SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 47187625b24SNicolin Chen int ret; 47287625b24SNicolin Chen 47387625b24SNicolin Chen mutex_lock(&ina->lock); 47487625b24SNicolin Chen 475d4b0166dSNicolin Chen switch (type) { 4765c090abfSNicolin Chen case hwmon_chip: 4775c090abfSNicolin Chen ret = ina3221_write_chip(dev, attr, val); 4785c090abfSNicolin Chen break; 479d4b0166dSNicolin Chen case hwmon_in: 480d4b0166dSNicolin Chen /* 0-align channel ID */ 48187625b24SNicolin Chen ret = ina3221_write_enable(dev, channel - 1, val); 48287625b24SNicolin Chen break; 483d4b0166dSNicolin Chen case hwmon_curr: 48487625b24SNicolin Chen ret = ina3221_write_curr(dev, attr, channel, val); 48587625b24SNicolin Chen break; 486d4b0166dSNicolin Chen default: 48787625b24SNicolin Chen ret = -EOPNOTSUPP; 48887625b24SNicolin Chen break; 489d4b0166dSNicolin Chen } 49087625b24SNicolin Chen 49187625b24SNicolin Chen mutex_unlock(&ina->lock); 49287625b24SNicolin Chen 49387625b24SNicolin Chen return ret; 494d4b0166dSNicolin Chen } 495d4b0166dSNicolin Chen 496d4b0166dSNicolin Chen static int ina3221_read_string(struct device *dev, enum hwmon_sensor_types type, 497d4b0166dSNicolin Chen u32 attr, int channel, const char **str) 498d4b0166dSNicolin Chen { 499d4b0166dSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 500d4b0166dSNicolin Chen int index = channel - 1; 501d4b0166dSNicolin Chen 502d4b0166dSNicolin Chen *str = ina->inputs[index].label; 503d4b0166dSNicolin Chen 504d4b0166dSNicolin Chen return 0; 505d4b0166dSNicolin Chen } 506d4b0166dSNicolin Chen 507d4b0166dSNicolin Chen static umode_t ina3221_is_visible(const void *drvdata, 508d4b0166dSNicolin Chen enum hwmon_sensor_types type, 509d4b0166dSNicolin Chen u32 attr, int channel) 510d4b0166dSNicolin Chen { 511d4b0166dSNicolin Chen const struct ina3221_data *ina = drvdata; 512d4b0166dSNicolin Chen const struct ina3221_input *input = NULL; 513d4b0166dSNicolin Chen 514d4b0166dSNicolin Chen switch (type) { 5155c090abfSNicolin Chen case hwmon_chip: 5165c090abfSNicolin Chen switch (attr) { 5175c090abfSNicolin Chen case hwmon_chip_samples: 518023912dbSNicolin Chen case hwmon_chip_update_interval: 5195c090abfSNicolin Chen return 0644; 5205c090abfSNicolin Chen default: 5215c090abfSNicolin Chen return 0; 5225c090abfSNicolin Chen } 523d4b0166dSNicolin Chen case hwmon_in: 524d4b0166dSNicolin Chen /* Ignore in0_ */ 525d4b0166dSNicolin Chen if (channel == 0) 526d4b0166dSNicolin Chen return 0; 527d4b0166dSNicolin Chen 528d4b0166dSNicolin Chen switch (attr) { 529d4b0166dSNicolin Chen case hwmon_in_label: 530d4b0166dSNicolin Chen if (channel - 1 <= INA3221_CHANNEL3) 531d4b0166dSNicolin Chen input = &ina->inputs[channel - 1]; 532d4b0166dSNicolin Chen /* Hide label node if label is not provided */ 533d4b0166dSNicolin Chen return (input && input->label) ? 0444 : 0; 534d4b0166dSNicolin Chen case hwmon_in_input: 535d4b0166dSNicolin Chen return 0444; 536d4b0166dSNicolin Chen case hwmon_in_enable: 537d4b0166dSNicolin Chen return 0644; 538d4b0166dSNicolin Chen default: 539d4b0166dSNicolin Chen return 0; 540d4b0166dSNicolin Chen } 541d4b0166dSNicolin Chen case hwmon_curr: 542d4b0166dSNicolin Chen switch (attr) { 543d4b0166dSNicolin Chen case hwmon_curr_input: 544d4b0166dSNicolin Chen case hwmon_curr_crit_alarm: 545d4b0166dSNicolin Chen case hwmon_curr_max_alarm: 546d4b0166dSNicolin Chen return 0444; 547d4b0166dSNicolin Chen case hwmon_curr_crit: 548d4b0166dSNicolin Chen case hwmon_curr_max: 549d4b0166dSNicolin Chen return 0644; 550d4b0166dSNicolin Chen default: 551d4b0166dSNicolin Chen return 0; 552d4b0166dSNicolin Chen } 553d4b0166dSNicolin Chen default: 554d4b0166dSNicolin Chen return 0; 555d4b0166dSNicolin Chen } 556d4b0166dSNicolin Chen } 557d4b0166dSNicolin Chen 5586f307b7cSGuenter Roeck #define INA3221_HWMON_CURR_CONFIG (HWMON_C_INPUT | \ 5596f307b7cSGuenter Roeck HWMON_C_CRIT | HWMON_C_CRIT_ALARM | \ 5606f307b7cSGuenter Roeck HWMON_C_MAX | HWMON_C_MAX_ALARM) 5616f307b7cSGuenter Roeck 5626f307b7cSGuenter Roeck static const struct hwmon_channel_info *ina3221_info[] = { 5635c090abfSNicolin Chen HWMON_CHANNEL_INFO(chip, 564023912dbSNicolin Chen HWMON_C_SAMPLES, 565023912dbSNicolin Chen HWMON_C_UPDATE_INTERVAL), 5666f307b7cSGuenter Roeck HWMON_CHANNEL_INFO(in, 567d4b0166dSNicolin Chen /* 0: dummy, skipped in is_visible */ 568d4b0166dSNicolin Chen HWMON_I_INPUT, 569d4b0166dSNicolin Chen /* 1-3: input voltage Channels */ 570d4b0166dSNicolin Chen HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, 571d4b0166dSNicolin Chen HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, 572d4b0166dSNicolin Chen HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, 573d4b0166dSNicolin Chen /* 4-6: shunt voltage Channels */ 574d4b0166dSNicolin Chen HWMON_I_INPUT, 575d4b0166dSNicolin Chen HWMON_I_INPUT, 5766f307b7cSGuenter Roeck HWMON_I_INPUT), 5776f307b7cSGuenter Roeck HWMON_CHANNEL_INFO(curr, 578d4b0166dSNicolin Chen INA3221_HWMON_CURR_CONFIG, 579d4b0166dSNicolin Chen INA3221_HWMON_CURR_CONFIG, 5806f307b7cSGuenter Roeck INA3221_HWMON_CURR_CONFIG), 581d4b0166dSNicolin Chen NULL 582d4b0166dSNicolin Chen }; 583d4b0166dSNicolin Chen 584d4b0166dSNicolin Chen static const struct hwmon_ops ina3221_hwmon_ops = { 585d4b0166dSNicolin Chen .is_visible = ina3221_is_visible, 586d4b0166dSNicolin Chen .read_string = ina3221_read_string, 587d4b0166dSNicolin Chen .read = ina3221_read, 588d4b0166dSNicolin Chen .write = ina3221_write, 589d4b0166dSNicolin Chen }; 590d4b0166dSNicolin Chen 591d4b0166dSNicolin Chen static const struct hwmon_chip_info ina3221_chip_info = { 592d4b0166dSNicolin Chen .ops = &ina3221_hwmon_ops, 593d4b0166dSNicolin Chen .info = ina3221_info, 594d4b0166dSNicolin Chen }; 595d4b0166dSNicolin Chen 596d4b0166dSNicolin Chen /* Extra attribute groups */ 597a4ec92edSGuenter Roeck static ssize_t ina3221_shunt_show(struct device *dev, 5987cb6dcffSAndrew F. Davis struct device_attribute *attr, char *buf) 5997cb6dcffSAndrew F. Davis { 6007cb6dcffSAndrew F. Davis struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); 6017cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 6027cb6dcffSAndrew F. Davis unsigned int channel = sd_attr->index; 603a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 6047cb6dcffSAndrew F. Davis 605a9e9dd9cSNicolin Chen return snprintf(buf, PAGE_SIZE, "%d\n", input->shunt_resistor); 6067cb6dcffSAndrew F. Davis } 6077cb6dcffSAndrew F. Davis 608a4ec92edSGuenter Roeck static ssize_t ina3221_shunt_store(struct device *dev, 6097cb6dcffSAndrew F. Davis struct device_attribute *attr, 6107cb6dcffSAndrew F. Davis const char *buf, size_t count) 6117cb6dcffSAndrew F. Davis { 6127cb6dcffSAndrew F. Davis struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); 6137cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 6147cb6dcffSAndrew F. Davis unsigned int channel = sd_attr->index; 615a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 6169ad0df1aSGuenter Roeck int val; 6177cb6dcffSAndrew F. Davis int ret; 6187cb6dcffSAndrew F. Davis 6199ad0df1aSGuenter Roeck ret = kstrtoint(buf, 0, &val); 6207cb6dcffSAndrew F. Davis if (ret) 6217cb6dcffSAndrew F. Davis return ret; 6227cb6dcffSAndrew F. Davis 6239ad0df1aSGuenter Roeck val = clamp_val(val, 1, INT_MAX); 6247cb6dcffSAndrew F. Davis 625a9e9dd9cSNicolin Chen input->shunt_resistor = val; 6267cb6dcffSAndrew F. Davis 6277cb6dcffSAndrew F. Davis return count; 6287cb6dcffSAndrew F. Davis } 6297cb6dcffSAndrew F. Davis 6307cb6dcffSAndrew F. Davis /* shunt resistance */ 631a4ec92edSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(shunt1_resistor, ina3221_shunt, INA3221_CHANNEL1); 632a4ec92edSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(shunt2_resistor, ina3221_shunt, INA3221_CHANNEL2); 633a4ec92edSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(shunt3_resistor, ina3221_shunt, INA3221_CHANNEL3); 6347cb6dcffSAndrew F. Davis 6357cb6dcffSAndrew F. Davis static struct attribute *ina3221_attrs[] = { 6367cb6dcffSAndrew F. Davis &sensor_dev_attr_shunt1_resistor.dev_attr.attr, 6377cb6dcffSAndrew F. Davis &sensor_dev_attr_shunt2_resistor.dev_attr.attr, 6387cb6dcffSAndrew F. Davis &sensor_dev_attr_shunt3_resistor.dev_attr.attr, 6397cb6dcffSAndrew F. Davis NULL, 6407cb6dcffSAndrew F. Davis }; 641d4b0166dSNicolin Chen ATTRIBUTE_GROUPS(ina3221); 6427cb6dcffSAndrew F. Davis 6437cb6dcffSAndrew F. Davis static const struct regmap_range ina3221_yes_ranges[] = { 644c20217b3SNicolin Chen regmap_reg_range(INA3221_CONFIG, INA3221_BUS3), 6457cb6dcffSAndrew F. Davis regmap_reg_range(INA3221_MASK_ENABLE, INA3221_MASK_ENABLE), 6467cb6dcffSAndrew F. Davis }; 6477cb6dcffSAndrew F. Davis 6487cb6dcffSAndrew F. Davis static const struct regmap_access_table ina3221_volatile_table = { 6497cb6dcffSAndrew F. Davis .yes_ranges = ina3221_yes_ranges, 6507cb6dcffSAndrew F. Davis .n_yes_ranges = ARRAY_SIZE(ina3221_yes_ranges), 6517cb6dcffSAndrew F. Davis }; 6527cb6dcffSAndrew F. Davis 6537cb6dcffSAndrew F. Davis static const struct regmap_config ina3221_regmap_config = { 6547cb6dcffSAndrew F. Davis .reg_bits = 8, 6557cb6dcffSAndrew F. Davis .val_bits = 16, 6567cb6dcffSAndrew F. Davis 6577cb6dcffSAndrew F. Davis .cache_type = REGCACHE_RBTREE, 6587cb6dcffSAndrew F. Davis .volatile_table = &ina3221_volatile_table, 6597cb6dcffSAndrew F. Davis }; 6607cb6dcffSAndrew F. Davis 661a9e9dd9cSNicolin Chen static int ina3221_probe_child_from_dt(struct device *dev, 662a9e9dd9cSNicolin Chen struct device_node *child, 663a9e9dd9cSNicolin Chen struct ina3221_data *ina) 664a9e9dd9cSNicolin Chen { 665a9e9dd9cSNicolin Chen struct ina3221_input *input; 666a9e9dd9cSNicolin Chen u32 val; 667a9e9dd9cSNicolin Chen int ret; 668a9e9dd9cSNicolin Chen 669a9e9dd9cSNicolin Chen ret = of_property_read_u32(child, "reg", &val); 670a9e9dd9cSNicolin Chen if (ret) { 6711b1f4efaSRob Herring dev_err(dev, "missing reg property of %pOFn\n", child); 672a9e9dd9cSNicolin Chen return ret; 673a9e9dd9cSNicolin Chen } else if (val > INA3221_CHANNEL3) { 6741b1f4efaSRob Herring dev_err(dev, "invalid reg %d of %pOFn\n", val, child); 675a9e9dd9cSNicolin Chen return ret; 676a9e9dd9cSNicolin Chen } 677a9e9dd9cSNicolin Chen 678a9e9dd9cSNicolin Chen input = &ina->inputs[val]; 679a9e9dd9cSNicolin Chen 680a9e9dd9cSNicolin Chen /* Log the disconnected channel input */ 681a9e9dd9cSNicolin Chen if (!of_device_is_available(child)) { 682a9e9dd9cSNicolin Chen input->disconnected = true; 683a9e9dd9cSNicolin Chen return 0; 684a9e9dd9cSNicolin Chen } 685a9e9dd9cSNicolin Chen 686a9e9dd9cSNicolin Chen /* Save the connected input label if available */ 687a9e9dd9cSNicolin Chen of_property_read_string(child, "label", &input->label); 688a9e9dd9cSNicolin Chen 689a9e9dd9cSNicolin Chen /* Overwrite default shunt resistor value optionally */ 690a6e43263SNicolin Chen if (!of_property_read_u32(child, "shunt-resistor-micro-ohms", &val)) { 691a6e43263SNicolin Chen if (val < 1 || val > INT_MAX) { 6921b1f4efaSRob Herring dev_err(dev, "invalid shunt resistor value %u of %pOFn\n", 6931b1f4efaSRob Herring val, child); 694a6e43263SNicolin Chen return -EINVAL; 695a6e43263SNicolin Chen } 696a9e9dd9cSNicolin Chen input->shunt_resistor = val; 697a6e43263SNicolin Chen } 698a9e9dd9cSNicolin Chen 699a9e9dd9cSNicolin Chen return 0; 700a9e9dd9cSNicolin Chen } 701a9e9dd9cSNicolin Chen 702a9e9dd9cSNicolin Chen static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina) 703a9e9dd9cSNicolin Chen { 704a9e9dd9cSNicolin Chen const struct device_node *np = dev->of_node; 705a9e9dd9cSNicolin Chen struct device_node *child; 706a9e9dd9cSNicolin Chen int ret; 707a9e9dd9cSNicolin Chen 708a9e9dd9cSNicolin Chen /* Compatible with non-DT platforms */ 709a9e9dd9cSNicolin Chen if (!np) 710a9e9dd9cSNicolin Chen return 0; 711a9e9dd9cSNicolin Chen 71243dece16SNicolin Chen ina->single_shot = of_property_read_bool(np, "ti,single-shot"); 71343dece16SNicolin Chen 714a9e9dd9cSNicolin Chen for_each_child_of_node(np, child) { 715a9e9dd9cSNicolin Chen ret = ina3221_probe_child_from_dt(dev, child, ina); 7169f754657SNishka Dasgupta if (ret) { 7179f754657SNishka Dasgupta of_node_put(child); 718a9e9dd9cSNicolin Chen return ret; 719a9e9dd9cSNicolin Chen } 7209f754657SNishka Dasgupta } 721a9e9dd9cSNicolin Chen 722a9e9dd9cSNicolin Chen return 0; 723a9e9dd9cSNicolin Chen } 724a9e9dd9cSNicolin Chen 7257cb6dcffSAndrew F. Davis static int ina3221_probe(struct i2c_client *client, 7267cb6dcffSAndrew F. Davis const struct i2c_device_id *id) 7277cb6dcffSAndrew F. Davis { 7287cb6dcffSAndrew F. Davis struct device *dev = &client->dev; 7297cb6dcffSAndrew F. Davis struct ina3221_data *ina; 7307cb6dcffSAndrew F. Davis struct device *hwmon_dev; 7317cb6dcffSAndrew F. Davis int i, ret; 7327cb6dcffSAndrew F. Davis 7337cb6dcffSAndrew F. Davis ina = devm_kzalloc(dev, sizeof(*ina), GFP_KERNEL); 7347cb6dcffSAndrew F. Davis if (!ina) 7357cb6dcffSAndrew F. Davis return -ENOMEM; 7367cb6dcffSAndrew F. Davis 7377cb6dcffSAndrew F. Davis ina->regmap = devm_regmap_init_i2c(client, &ina3221_regmap_config); 7387cb6dcffSAndrew F. Davis if (IS_ERR(ina->regmap)) { 7397cb6dcffSAndrew F. Davis dev_err(dev, "Unable to allocate register map\n"); 7407cb6dcffSAndrew F. Davis return PTR_ERR(ina->regmap); 7417cb6dcffSAndrew F. Davis } 7427cb6dcffSAndrew F. Davis 7437cb6dcffSAndrew F. Davis for (i = 0; i < F_MAX_FIELDS; i++) { 7447cb6dcffSAndrew F. Davis ina->fields[i] = devm_regmap_field_alloc(dev, 7457cb6dcffSAndrew F. Davis ina->regmap, 7467cb6dcffSAndrew F. Davis ina3221_reg_fields[i]); 7477cb6dcffSAndrew F. Davis if (IS_ERR(ina->fields[i])) { 7487cb6dcffSAndrew F. Davis dev_err(dev, "Unable to allocate regmap fields\n"); 7497cb6dcffSAndrew F. Davis return PTR_ERR(ina->fields[i]); 7507cb6dcffSAndrew F. Davis } 7517cb6dcffSAndrew F. Davis } 7527cb6dcffSAndrew F. Davis 7537cb6dcffSAndrew F. Davis for (i = 0; i < INA3221_NUM_CHANNELS; i++) 754a9e9dd9cSNicolin Chen ina->inputs[i].shunt_resistor = INA3221_RSHUNT_DEFAULT; 755a9e9dd9cSNicolin Chen 756a9e9dd9cSNicolin Chen ret = ina3221_probe_from_dt(dev, ina); 757a9e9dd9cSNicolin Chen if (ret) { 758a9e9dd9cSNicolin Chen dev_err(dev, "Unable to probe from device tree\n"); 759a9e9dd9cSNicolin Chen return ret; 760a9e9dd9cSNicolin Chen } 7617cb6dcffSAndrew F. Davis 762323aeb0eSNicolin Chen /* The driver will be reset, so use reset value */ 763323aeb0eSNicolin Chen ina->reg_config = INA3221_CONFIG_DEFAULT; 764a9e9dd9cSNicolin Chen 76543dece16SNicolin Chen /* Clear continuous bit to use single-shot mode */ 76643dece16SNicolin Chen if (ina->single_shot) 76743dece16SNicolin Chen ina->reg_config &= ~INA3221_CONFIG_MODE_CONTINUOUS; 76843dece16SNicolin Chen 769a9e9dd9cSNicolin Chen /* Disable channels if their inputs are disconnected */ 770a9e9dd9cSNicolin Chen for (i = 0; i < INA3221_NUM_CHANNELS; i++) { 771a9e9dd9cSNicolin Chen if (ina->inputs[i].disconnected) 772a9e9dd9cSNicolin Chen ina->reg_config &= ~INA3221_CONFIG_CHx_EN(i); 773a9e9dd9cSNicolin Chen } 774a9e9dd9cSNicolin Chen 775323aeb0eSNicolin Chen ina->pm_dev = dev; 77687625b24SNicolin Chen mutex_init(&ina->lock); 77759d608e1SNicolin Chen dev_set_drvdata(dev, ina); 77859d608e1SNicolin Chen 779323aeb0eSNicolin Chen /* Enable PM runtime -- status is suspended by default */ 780323aeb0eSNicolin Chen pm_runtime_enable(ina->pm_dev); 781323aeb0eSNicolin Chen 782323aeb0eSNicolin Chen /* Initialize (resume) the device */ 783323aeb0eSNicolin Chen for (i = 0; i < INA3221_NUM_CHANNELS; i++) { 784323aeb0eSNicolin Chen if (ina->inputs[i].disconnected) 785323aeb0eSNicolin Chen continue; 786323aeb0eSNicolin Chen /* Match the refcount with number of enabled channels */ 787323aeb0eSNicolin Chen ret = pm_runtime_get_sync(ina->pm_dev); 788323aeb0eSNicolin Chen if (ret < 0) 789323aeb0eSNicolin Chen goto fail; 790323aeb0eSNicolin Chen } 791323aeb0eSNicolin Chen 792d4b0166dSNicolin Chen hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, ina, 793d4b0166dSNicolin Chen &ina3221_chip_info, 794d4b0166dSNicolin Chen ina3221_groups); 7957cb6dcffSAndrew F. Davis if (IS_ERR(hwmon_dev)) { 7967cb6dcffSAndrew F. Davis dev_err(dev, "Unable to register hwmon device\n"); 797323aeb0eSNicolin Chen ret = PTR_ERR(hwmon_dev); 798323aeb0eSNicolin Chen goto fail; 7997cb6dcffSAndrew F. Davis } 8007cb6dcffSAndrew F. Davis 8017cb6dcffSAndrew F. Davis return 0; 802323aeb0eSNicolin Chen 803323aeb0eSNicolin Chen fail: 804323aeb0eSNicolin Chen pm_runtime_disable(ina->pm_dev); 805323aeb0eSNicolin Chen pm_runtime_set_suspended(ina->pm_dev); 806323aeb0eSNicolin Chen /* pm_runtime_put_noidle() will decrease the PM refcount until 0 */ 807323aeb0eSNicolin Chen for (i = 0; i < INA3221_NUM_CHANNELS; i++) 808323aeb0eSNicolin Chen pm_runtime_put_noidle(ina->pm_dev); 809323aeb0eSNicolin Chen mutex_destroy(&ina->lock); 810323aeb0eSNicolin Chen 811323aeb0eSNicolin Chen return ret; 8127cb6dcffSAndrew F. Davis } 8137cb6dcffSAndrew F. Davis 81487625b24SNicolin Chen static int ina3221_remove(struct i2c_client *client) 81587625b24SNicolin Chen { 81687625b24SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(&client->dev); 817323aeb0eSNicolin Chen int i; 818323aeb0eSNicolin Chen 819323aeb0eSNicolin Chen pm_runtime_disable(ina->pm_dev); 820323aeb0eSNicolin Chen pm_runtime_set_suspended(ina->pm_dev); 821323aeb0eSNicolin Chen 822323aeb0eSNicolin Chen /* pm_runtime_put_noidle() will decrease the PM refcount until 0 */ 823323aeb0eSNicolin Chen for (i = 0; i < INA3221_NUM_CHANNELS; i++) 824323aeb0eSNicolin Chen pm_runtime_put_noidle(ina->pm_dev); 82587625b24SNicolin Chen 82687625b24SNicolin Chen mutex_destroy(&ina->lock); 82787625b24SNicolin Chen 82887625b24SNicolin Chen return 0; 82987625b24SNicolin Chen } 83087625b24SNicolin Chen 831ead21c77SArnd Bergmann static int __maybe_unused ina3221_suspend(struct device *dev) 83259d608e1SNicolin Chen { 83359d608e1SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 83459d608e1SNicolin Chen int ret; 83559d608e1SNicolin Chen 83659d608e1SNicolin Chen /* Save config register value and enable cache-only */ 83759d608e1SNicolin Chen ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config); 83859d608e1SNicolin Chen if (ret) 83959d608e1SNicolin Chen return ret; 84059d608e1SNicolin Chen 84159d608e1SNicolin Chen /* Set to power-down mode for power saving */ 84259d608e1SNicolin Chen ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, 84359d608e1SNicolin Chen INA3221_CONFIG_MODE_MASK, 84459d608e1SNicolin Chen INA3221_CONFIG_MODE_POWERDOWN); 84559d608e1SNicolin Chen if (ret) 84659d608e1SNicolin Chen return ret; 84759d608e1SNicolin Chen 84859d608e1SNicolin Chen regcache_cache_only(ina->regmap, true); 84959d608e1SNicolin Chen regcache_mark_dirty(ina->regmap); 85059d608e1SNicolin Chen 85159d608e1SNicolin Chen return 0; 85259d608e1SNicolin Chen } 85359d608e1SNicolin Chen 854ead21c77SArnd Bergmann static int __maybe_unused ina3221_resume(struct device *dev) 85559d608e1SNicolin Chen { 85659d608e1SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 85759d608e1SNicolin Chen int ret; 85859d608e1SNicolin Chen 85959d608e1SNicolin Chen regcache_cache_only(ina->regmap, false); 86059d608e1SNicolin Chen 86159d608e1SNicolin Chen /* Software reset the chip */ 86259d608e1SNicolin Chen ret = regmap_field_write(ina->fields[F_RST], true); 86359d608e1SNicolin Chen if (ret) { 86459d608e1SNicolin Chen dev_err(dev, "Unable to reset device\n"); 86559d608e1SNicolin Chen return ret; 86659d608e1SNicolin Chen } 86759d608e1SNicolin Chen 86859d608e1SNicolin Chen /* Restore cached register values to hardware */ 86959d608e1SNicolin Chen ret = regcache_sync(ina->regmap); 87059d608e1SNicolin Chen if (ret) 87159d608e1SNicolin Chen return ret; 87259d608e1SNicolin Chen 87359d608e1SNicolin Chen /* Restore config register value to hardware */ 87459d608e1SNicolin Chen ret = regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config); 87559d608e1SNicolin Chen if (ret) 87659d608e1SNicolin Chen return ret; 87759d608e1SNicolin Chen 87859d608e1SNicolin Chen return 0; 87959d608e1SNicolin Chen } 88059d608e1SNicolin Chen 88159d608e1SNicolin Chen static const struct dev_pm_ops ina3221_pm = { 882323aeb0eSNicolin Chen SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 883323aeb0eSNicolin Chen pm_runtime_force_resume) 884323aeb0eSNicolin Chen SET_RUNTIME_PM_OPS(ina3221_suspend, ina3221_resume, NULL) 88559d608e1SNicolin Chen }; 88659d608e1SNicolin Chen 8877cb6dcffSAndrew F. Davis static const struct of_device_id ina3221_of_match_table[] = { 8887cb6dcffSAndrew F. Davis { .compatible = "ti,ina3221", }, 8897cb6dcffSAndrew F. Davis { /* sentinel */ } 8907cb6dcffSAndrew F. Davis }; 8917cb6dcffSAndrew F. Davis MODULE_DEVICE_TABLE(of, ina3221_of_match_table); 8927cb6dcffSAndrew F. Davis 8937cb6dcffSAndrew F. Davis static const struct i2c_device_id ina3221_ids[] = { 8947cb6dcffSAndrew F. Davis { "ina3221", 0 }, 8957cb6dcffSAndrew F. Davis { /* sentinel */ } 8967cb6dcffSAndrew F. Davis }; 8977cb6dcffSAndrew F. Davis MODULE_DEVICE_TABLE(i2c, ina3221_ids); 8987cb6dcffSAndrew F. Davis 8997cb6dcffSAndrew F. Davis static struct i2c_driver ina3221_i2c_driver = { 9007cb6dcffSAndrew F. Davis .probe = ina3221_probe, 90187625b24SNicolin Chen .remove = ina3221_remove, 9027cb6dcffSAndrew F. Davis .driver = { 9037cb6dcffSAndrew F. Davis .name = INA3221_DRIVER_NAME, 9047cb6dcffSAndrew F. Davis .of_match_table = ina3221_of_match_table, 90559d608e1SNicolin Chen .pm = &ina3221_pm, 9067cb6dcffSAndrew F. Davis }, 9077cb6dcffSAndrew F. Davis .id_table = ina3221_ids, 9087cb6dcffSAndrew F. Davis }; 9097cb6dcffSAndrew F. Davis module_i2c_driver(ina3221_i2c_driver); 9107cb6dcffSAndrew F. Davis 9117cb6dcffSAndrew F. Davis MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); 9127cb6dcffSAndrew F. Davis MODULE_DESCRIPTION("Texas Instruments INA3221 HWMon Driver"); 9137cb6dcffSAndrew F. Davis MODULE_LICENSE("GPL v2"); 914