11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 27cb6dcffSAndrew F. Davis /* 37cb6dcffSAndrew F. Davis * INA3221 Triple Current/Voltage Monitor 47cb6dcffSAndrew F. Davis * 5ad736c1aSAlexander A. Klimov * Copyright (C) 2016 Texas Instruments Incorporated - https://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 342057bdfbSNicolin Chen #define INA3221_SHUNT_SUM 0x0d 352057bdfbSNicolin Chen #define INA3221_CRIT_SUM 0x0e 367cb6dcffSAndrew F. Davis #define INA3221_MASK_ENABLE 0x0f 377cb6dcffSAndrew F. Davis 3859d608e1SNicolin Chen #define INA3221_CONFIG_MODE_MASK GENMASK(2, 0) 3959d608e1SNicolin Chen #define INA3221_CONFIG_MODE_POWERDOWN 0 40791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_SHUNT BIT(0) 41791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_BUS BIT(1) 42791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_CONTINUOUS BIT(2) 434c0415a3SNicolin Chen #define INA3221_CONFIG_VSH_CT_SHIFT 3 444c0415a3SNicolin Chen #define INA3221_CONFIG_VSH_CT_MASK GENMASK(5, 3) 454c0415a3SNicolin Chen #define INA3221_CONFIG_VSH_CT(x) (((x) & GENMASK(5, 3)) >> 3) 464c0415a3SNicolin Chen #define INA3221_CONFIG_VBUS_CT_SHIFT 6 474c0415a3SNicolin Chen #define INA3221_CONFIG_VBUS_CT_MASK GENMASK(8, 6) 484c0415a3SNicolin Chen #define INA3221_CONFIG_VBUS_CT(x) (((x) & GENMASK(8, 6)) >> 6) 495c090abfSNicolin Chen #define INA3221_CONFIG_AVG_SHIFT 9 505c090abfSNicolin Chen #define INA3221_CONFIG_AVG_MASK GENMASK(11, 9) 515c090abfSNicolin Chen #define INA3221_CONFIG_AVG(x) (((x) & GENMASK(11, 9)) >> 9) 524c0415a3SNicolin Chen #define INA3221_CONFIG_CHs_EN_MASK GENMASK(14, 12) 53a9e9dd9cSNicolin Chen #define INA3221_CONFIG_CHx_EN(x) BIT(14 - (x)) 547cb6dcffSAndrew F. Davis 552057bdfbSNicolin Chen #define INA3221_MASK_ENABLE_SCC_MASK GENMASK(14, 12) 562057bdfbSNicolin Chen 57323aeb0eSNicolin Chen #define INA3221_CONFIG_DEFAULT 0x7127 587cb6dcffSAndrew F. Davis #define INA3221_RSHUNT_DEFAULT 10000 597cb6dcffSAndrew F. Davis 607cb6dcffSAndrew F. Davis enum ina3221_fields { 617cb6dcffSAndrew F. Davis /* Configuration */ 627cb6dcffSAndrew F. Davis F_RST, 637cb6dcffSAndrew F. Davis 644c0415a3SNicolin Chen /* Status Flags */ 654c0415a3SNicolin Chen F_CVRF, 664c0415a3SNicolin Chen 672057bdfbSNicolin Chen /* Warning Flags */ 687cb6dcffSAndrew F. Davis F_WF3, F_WF2, F_WF1, 692057bdfbSNicolin Chen 702057bdfbSNicolin Chen /* Alert Flags: SF is the summation-alert flag */ 712057bdfbSNicolin Chen F_SF, F_CF3, F_CF2, F_CF1, 727cb6dcffSAndrew F. Davis 737cb6dcffSAndrew F. Davis /* sentinel */ 747cb6dcffSAndrew F. Davis F_MAX_FIELDS 757cb6dcffSAndrew F. Davis }; 767cb6dcffSAndrew F. Davis 777cb6dcffSAndrew F. Davis static const struct reg_field ina3221_reg_fields[] = { 787cb6dcffSAndrew F. Davis [F_RST] = REG_FIELD(INA3221_CONFIG, 15, 15), 797cb6dcffSAndrew F. Davis 804c0415a3SNicolin Chen [F_CVRF] = REG_FIELD(INA3221_MASK_ENABLE, 0, 0), 817cb6dcffSAndrew F. Davis [F_WF3] = REG_FIELD(INA3221_MASK_ENABLE, 3, 3), 827cb6dcffSAndrew F. Davis [F_WF2] = REG_FIELD(INA3221_MASK_ENABLE, 4, 4), 837cb6dcffSAndrew F. Davis [F_WF1] = REG_FIELD(INA3221_MASK_ENABLE, 5, 5), 842057bdfbSNicolin Chen [F_SF] = REG_FIELD(INA3221_MASK_ENABLE, 6, 6), 857cb6dcffSAndrew F. Davis [F_CF3] = REG_FIELD(INA3221_MASK_ENABLE, 7, 7), 867cb6dcffSAndrew F. Davis [F_CF2] = REG_FIELD(INA3221_MASK_ENABLE, 8, 8), 877cb6dcffSAndrew F. Davis [F_CF1] = REG_FIELD(INA3221_MASK_ENABLE, 9, 9), 887cb6dcffSAndrew F. Davis }; 897cb6dcffSAndrew F. Davis 907cb6dcffSAndrew F. Davis enum ina3221_channels { 917cb6dcffSAndrew F. Davis INA3221_CHANNEL1, 927cb6dcffSAndrew F. Davis INA3221_CHANNEL2, 937cb6dcffSAndrew F. Davis INA3221_CHANNEL3, 947cb6dcffSAndrew F. Davis INA3221_NUM_CHANNELS 957cb6dcffSAndrew F. Davis }; 967cb6dcffSAndrew F. Davis 977cb6dcffSAndrew F. Davis /** 98a9e9dd9cSNicolin Chen * struct ina3221_input - channel input source specific information 99a9e9dd9cSNicolin Chen * @label: label of channel input source 100a9e9dd9cSNicolin Chen * @shunt_resistor: shunt resistor value of channel input source 101a9e9dd9cSNicolin Chen * @disconnected: connection status of channel input source 102a9e9dd9cSNicolin Chen */ 103a9e9dd9cSNicolin Chen struct ina3221_input { 104a9e9dd9cSNicolin Chen const char *label; 105a9e9dd9cSNicolin Chen int shunt_resistor; 106a9e9dd9cSNicolin Chen bool disconnected; 107a9e9dd9cSNicolin Chen }; 108a9e9dd9cSNicolin Chen 109a9e9dd9cSNicolin Chen /** 1107cb6dcffSAndrew F. Davis * struct ina3221_data - device specific information 111323aeb0eSNicolin Chen * @pm_dev: Device pointer for pm runtime 1127cb6dcffSAndrew F. Davis * @regmap: Register map of the device 1137cb6dcffSAndrew F. Davis * @fields: Register fields of the device 114a9e9dd9cSNicolin Chen * @inputs: Array of channel input source specific structures 11587625b24SNicolin Chen * @lock: mutex lock to serialize sysfs attribute accesses 11659d608e1SNicolin Chen * @reg_config: Register value of INA3221_CONFIG 1172057bdfbSNicolin Chen * @summation_shunt_resistor: equivalent shunt resistor value for summation 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; 1272057bdfbSNicolin Chen int summation_shunt_resistor; 12843dece16SNicolin Chen 12943dece16SNicolin Chen bool single_shot; 1307cb6dcffSAndrew F. Davis }; 1317cb6dcffSAndrew F. Davis 132a9e9dd9cSNicolin Chen static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel) 133a9e9dd9cSNicolin Chen { 1342057bdfbSNicolin Chen /* Summation channel checks shunt resistor values */ 1352057bdfbSNicolin Chen if (channel > INA3221_CHANNEL3) 1362057bdfbSNicolin Chen return ina->summation_shunt_resistor != 0; 1372057bdfbSNicolin Chen 138323aeb0eSNicolin Chen return pm_runtime_active(ina->pm_dev) && 139323aeb0eSNicolin Chen (ina->reg_config & INA3221_CONFIG_CHx_EN(channel)); 140a9e9dd9cSNicolin Chen } 141a9e9dd9cSNicolin Chen 1422057bdfbSNicolin Chen /** 1432057bdfbSNicolin Chen * Helper function to return the resistor value for current summation. 1442057bdfbSNicolin Chen * 1452057bdfbSNicolin Chen * There is a condition to calculate current summation -- all the shunt 1462057bdfbSNicolin Chen * resistor values should be the same, so as to simply fit the formula: 1472057bdfbSNicolin Chen * current summation = shunt voltage summation / shunt resistor 1482057bdfbSNicolin Chen * 1492057bdfbSNicolin Chen * Returns the equivalent shunt resistor value on success or 0 on failure 1502057bdfbSNicolin Chen */ 1512057bdfbSNicolin Chen static inline int ina3221_summation_shunt_resistor(struct ina3221_data *ina) 1522057bdfbSNicolin Chen { 1532057bdfbSNicolin Chen struct ina3221_input *input = ina->inputs; 1542057bdfbSNicolin Chen int i, shunt_resistor = 0; 1552057bdfbSNicolin Chen 1562057bdfbSNicolin Chen for (i = 0; i < INA3221_NUM_CHANNELS; i++) { 1572057bdfbSNicolin Chen if (input[i].disconnected || !input[i].shunt_resistor) 1582057bdfbSNicolin Chen continue; 1592057bdfbSNicolin Chen if (!shunt_resistor) { 1602057bdfbSNicolin Chen /* Found the reference shunt resistor value */ 1612057bdfbSNicolin Chen shunt_resistor = input[i].shunt_resistor; 1622057bdfbSNicolin Chen } else { 1632057bdfbSNicolin Chen /* No summation if resistor values are different */ 1642057bdfbSNicolin Chen if (shunt_resistor != input[i].shunt_resistor) 1652057bdfbSNicolin Chen return 0; 1662057bdfbSNicolin Chen } 1672057bdfbSNicolin Chen } 1682057bdfbSNicolin Chen 1692057bdfbSNicolin Chen return shunt_resistor; 1702057bdfbSNicolin Chen } 1712057bdfbSNicolin Chen 1724c0415a3SNicolin Chen /* Lookup table for Bus and Shunt conversion times in usec */ 1734c0415a3SNicolin Chen static const u16 ina3221_conv_time[] = { 1744c0415a3SNicolin Chen 140, 204, 332, 588, 1100, 2116, 4156, 8244, 1754c0415a3SNicolin Chen }; 1764c0415a3SNicolin Chen 1775c090abfSNicolin Chen /* Lookup table for number of samples using in averaging mode */ 1785c090abfSNicolin Chen static const int ina3221_avg_samples[] = { 1795c090abfSNicolin Chen 1, 4, 16, 64, 128, 256, 512, 1024, 1805c090abfSNicolin Chen }; 1815c090abfSNicolin Chen 182023912dbSNicolin Chen /* Converting update_interval in msec to conversion time in usec */ 183023912dbSNicolin Chen static inline u32 ina3221_interval_ms_to_conv_time(u16 config, int interval) 1844c0415a3SNicolin Chen { 185023912dbSNicolin Chen u32 channels = hweight16(config & INA3221_CONFIG_CHs_EN_MASK); 186023912dbSNicolin Chen u32 samples_idx = INA3221_CONFIG_AVG(config); 187023912dbSNicolin Chen u32 samples = ina3221_avg_samples[samples_idx]; 188023912dbSNicolin Chen 189023912dbSNicolin Chen /* Bisect the result to Bus and Shunt conversion times */ 190023912dbSNicolin Chen return DIV_ROUND_CLOSEST(interval * 1000 / 2, channels * samples); 191023912dbSNicolin Chen } 192023912dbSNicolin Chen 193023912dbSNicolin Chen /* Converting CONFIG register value to update_interval in usec */ 194023912dbSNicolin Chen static inline u32 ina3221_reg_to_interval_us(u16 config) 195023912dbSNicolin Chen { 196023912dbSNicolin Chen u32 channels = hweight16(config & INA3221_CONFIG_CHs_EN_MASK); 197023912dbSNicolin Chen u32 vbus_ct_idx = INA3221_CONFIG_VBUS_CT(config); 198023912dbSNicolin Chen u32 vsh_ct_idx = INA3221_CONFIG_VSH_CT(config); 199023912dbSNicolin Chen u32 samples_idx = INA3221_CONFIG_AVG(config); 2005c090abfSNicolin Chen u32 samples = ina3221_avg_samples[samples_idx]; 2014c0415a3SNicolin Chen u32 vbus_ct = ina3221_conv_time[vbus_ct_idx]; 2024c0415a3SNicolin Chen u32 vsh_ct = ina3221_conv_time[vsh_ct_idx]; 2034c0415a3SNicolin Chen 2044c0415a3SNicolin Chen /* Calculate total conversion time */ 205023912dbSNicolin Chen return channels * (vbus_ct + vsh_ct) * samples; 206023912dbSNicolin Chen } 207023912dbSNicolin Chen 208023912dbSNicolin Chen static inline int ina3221_wait_for_data(struct ina3221_data *ina) 209023912dbSNicolin Chen { 210023912dbSNicolin Chen u32 wait, cvrf; 211023912dbSNicolin Chen 212023912dbSNicolin Chen wait = ina3221_reg_to_interval_us(ina->reg_config); 2134c0415a3SNicolin Chen 2144c0415a3SNicolin Chen /* Polling the CVRF bit to make sure read data is ready */ 2154c0415a3SNicolin Chen return regmap_field_read_poll_timeout(ina->fields[F_CVRF], 2162ccb4f16SNicolin Chen cvrf, cvrf, wait, wait * 2); 2174c0415a3SNicolin Chen } 2184c0415a3SNicolin Chen 2197cb6dcffSAndrew F. Davis static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg, 2207cb6dcffSAndrew F. Davis int *val) 2217cb6dcffSAndrew F. Davis { 2227cb6dcffSAndrew F. Davis unsigned int regval; 2237cb6dcffSAndrew F. Davis int ret; 2247cb6dcffSAndrew F. Davis 2257cb6dcffSAndrew F. Davis ret = regmap_read(ina->regmap, reg, ®val); 2267cb6dcffSAndrew F. Davis if (ret) 2277cb6dcffSAndrew F. Davis return ret; 2287cb6dcffSAndrew F. Davis 2292057bdfbSNicolin Chen /* 2302057bdfbSNicolin Chen * Shunt Voltage Sum register has 14-bit value with 1-bit shift 2312057bdfbSNicolin Chen * Other Shunt Voltage registers have 12 bits with 3-bit shift 2322057bdfbSNicolin Chen */ 2332057bdfbSNicolin Chen if (reg == INA3221_SHUNT_SUM) 2342057bdfbSNicolin Chen *val = sign_extend32(regval >> 1, 14); 2352057bdfbSNicolin Chen else 2367cb6dcffSAndrew F. Davis *val = sign_extend32(regval >> 3, 12); 2377cb6dcffSAndrew F. Davis 2387cb6dcffSAndrew F. Davis return 0; 2397cb6dcffSAndrew F. Davis } 2407cb6dcffSAndrew F. Davis 241d4b0166dSNicolin Chen static const u8 ina3221_in_reg[] = { 242d4b0166dSNicolin Chen INA3221_BUS1, 243d4b0166dSNicolin Chen INA3221_BUS2, 244d4b0166dSNicolin Chen INA3221_BUS3, 245d4b0166dSNicolin Chen INA3221_SHUNT1, 246d4b0166dSNicolin Chen INA3221_SHUNT2, 247d4b0166dSNicolin Chen INA3221_SHUNT3, 2482057bdfbSNicolin Chen INA3221_SHUNT_SUM, 249d4b0166dSNicolin Chen }; 2507cb6dcffSAndrew F. Davis 2515c090abfSNicolin Chen static int ina3221_read_chip(struct device *dev, u32 attr, long *val) 2525c090abfSNicolin Chen { 2535c090abfSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 2545c090abfSNicolin Chen int regval; 2555c090abfSNicolin Chen 2565c090abfSNicolin Chen switch (attr) { 2575c090abfSNicolin Chen case hwmon_chip_samples: 2585c090abfSNicolin Chen regval = INA3221_CONFIG_AVG(ina->reg_config); 2595c090abfSNicolin Chen *val = ina3221_avg_samples[regval]; 2605c090abfSNicolin Chen return 0; 261023912dbSNicolin Chen case hwmon_chip_update_interval: 262023912dbSNicolin Chen /* Return in msec */ 263023912dbSNicolin Chen *val = ina3221_reg_to_interval_us(ina->reg_config); 264023912dbSNicolin Chen *val = DIV_ROUND_CLOSEST(*val, 1000); 265023912dbSNicolin Chen return 0; 2665c090abfSNicolin Chen default: 2675c090abfSNicolin Chen return -EOPNOTSUPP; 2685c090abfSNicolin Chen } 2695c090abfSNicolin Chen } 2705c090abfSNicolin Chen 271d4b0166dSNicolin Chen static int ina3221_read_in(struct device *dev, u32 attr, int channel, long *val) 272d4b0166dSNicolin Chen { 273d4b0166dSNicolin Chen const bool is_shunt = channel > INA3221_CHANNEL3; 274d4b0166dSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 275d4b0166dSNicolin Chen u8 reg = ina3221_in_reg[channel]; 276d4b0166dSNicolin Chen int regval, ret; 277d4b0166dSNicolin Chen 2782057bdfbSNicolin Chen /* 2792057bdfbSNicolin Chen * Translate shunt channel index to sensor channel index except 2802057bdfbSNicolin Chen * the 7th channel (6 since being 0-aligned) is for summation. 2812057bdfbSNicolin Chen */ 2822057bdfbSNicolin Chen if (channel != 6) 283d4b0166dSNicolin Chen channel %= INA3221_NUM_CHANNELS; 284d4b0166dSNicolin Chen 285d4b0166dSNicolin Chen switch (attr) { 286d4b0166dSNicolin Chen case hwmon_in_input: 287d4b0166dSNicolin Chen if (!ina3221_is_enabled(ina, channel)) 288a9e9dd9cSNicolin Chen return -ENODATA; 289a9e9dd9cSNicolin Chen 29043dece16SNicolin Chen /* Write CONFIG register to trigger a single-shot measurement */ 29143dece16SNicolin Chen if (ina->single_shot) 29243dece16SNicolin Chen regmap_write(ina->regmap, INA3221_CONFIG, 29343dece16SNicolin Chen ina->reg_config); 29443dece16SNicolin Chen 2954c0415a3SNicolin Chen ret = ina3221_wait_for_data(ina); 2964c0415a3SNicolin Chen if (ret) 2974c0415a3SNicolin Chen return ret; 2984c0415a3SNicolin Chen 299d4b0166dSNicolin Chen ret = ina3221_read_value(ina, reg, ®val); 3007cb6dcffSAndrew F. Davis if (ret) 3017cb6dcffSAndrew F. Davis return ret; 3027cb6dcffSAndrew F. Davis 303d4b0166dSNicolin Chen /* 304d4b0166dSNicolin Chen * Scale of shunt voltage (uV): LSB is 40uV 305d4b0166dSNicolin Chen * Scale of bus voltage (mV): LSB is 8mV 306d4b0166dSNicolin Chen */ 307d4b0166dSNicolin Chen *val = regval * (is_shunt ? 40 : 8); 308d4b0166dSNicolin Chen return 0; 309d4b0166dSNicolin Chen case hwmon_in_enable: 310d4b0166dSNicolin Chen *val = ina3221_is_enabled(ina, channel); 311d4b0166dSNicolin Chen return 0; 312d4b0166dSNicolin Chen default: 313d4b0166dSNicolin Chen return -EOPNOTSUPP; 314d4b0166dSNicolin Chen } 3157cb6dcffSAndrew F. Davis } 3167cb6dcffSAndrew F. Davis 3172057bdfbSNicolin Chen static const u8 ina3221_curr_reg[][INA3221_NUM_CHANNELS + 1] = { 3182057bdfbSNicolin Chen [hwmon_curr_input] = { INA3221_SHUNT1, INA3221_SHUNT2, 3192057bdfbSNicolin Chen INA3221_SHUNT3, INA3221_SHUNT_SUM }, 3202057bdfbSNicolin Chen [hwmon_curr_max] = { INA3221_WARN1, INA3221_WARN2, INA3221_WARN3, 0 }, 3212057bdfbSNicolin Chen [hwmon_curr_crit] = { INA3221_CRIT1, INA3221_CRIT2, 3222057bdfbSNicolin Chen INA3221_CRIT3, INA3221_CRIT_SUM }, 3232057bdfbSNicolin Chen [hwmon_curr_max_alarm] = { F_WF1, F_WF2, F_WF3, 0 }, 3242057bdfbSNicolin Chen [hwmon_curr_crit_alarm] = { F_CF1, F_CF2, F_CF3, F_SF }, 325d4b0166dSNicolin Chen }; 326d4b0166dSNicolin Chen 327d4b0166dSNicolin Chen static int ina3221_read_curr(struct device *dev, u32 attr, 328d4b0166dSNicolin Chen int channel, long *val) 3297cb6dcffSAndrew F. Davis { 3307cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 3312057bdfbSNicolin Chen struct ina3221_input *input = ina->inputs; 332d4b0166dSNicolin Chen u8 reg = ina3221_curr_reg[attr][channel]; 3332057bdfbSNicolin Chen int resistance_uo, voltage_nv; 3342057bdfbSNicolin Chen int regval, ret; 3352057bdfbSNicolin Chen 3362057bdfbSNicolin Chen if (channel > INA3221_CHANNEL3) 3372057bdfbSNicolin Chen resistance_uo = ina->summation_shunt_resistor; 3382057bdfbSNicolin Chen else 3392057bdfbSNicolin Chen resistance_uo = input[channel].shunt_resistor; 3407cb6dcffSAndrew F. Davis 341d4b0166dSNicolin Chen switch (attr) { 342d4b0166dSNicolin Chen case hwmon_curr_input: 343d4b0166dSNicolin Chen if (!ina3221_is_enabled(ina, channel)) 344a9e9dd9cSNicolin Chen return -ENODATA; 3454c0415a3SNicolin Chen 34643dece16SNicolin Chen /* Write CONFIG register to trigger a single-shot measurement */ 34743dece16SNicolin Chen if (ina->single_shot) 34843dece16SNicolin Chen regmap_write(ina->regmap, INA3221_CONFIG, 34943dece16SNicolin Chen ina->reg_config); 35043dece16SNicolin Chen 3514c0415a3SNicolin Chen ret = ina3221_wait_for_data(ina); 3524c0415a3SNicolin Chen if (ret) 3534c0415a3SNicolin Chen return ret; 3544c0415a3SNicolin Chen 355d4b0166dSNicolin Chen /* fall through */ 356d4b0166dSNicolin Chen case hwmon_curr_crit: 357d4b0166dSNicolin Chen case hwmon_curr_max: 3582057bdfbSNicolin Chen if (!resistance_uo) 3592057bdfbSNicolin Chen return -ENODATA; 3602057bdfbSNicolin Chen 361d4b0166dSNicolin Chen ret = ina3221_read_value(ina, reg, ®val); 3627cb6dcffSAndrew F. Davis if (ret) 3637cb6dcffSAndrew F. Davis return ret; 3647cb6dcffSAndrew F. Davis 365d4b0166dSNicolin Chen /* Scale of shunt voltage: LSB is 40uV (40000nV) */ 366d4b0166dSNicolin Chen voltage_nv = regval * 40000; 367d4b0166dSNicolin Chen /* Return current in mA */ 368d4b0166dSNicolin Chen *val = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo); 369d4b0166dSNicolin Chen return 0; 370d4b0166dSNicolin Chen case hwmon_curr_crit_alarm: 371d4b0166dSNicolin Chen case hwmon_curr_max_alarm: 372efb0489eSNicolin Chen /* No actual register read if channel is disabled */ 373efb0489eSNicolin Chen if (!ina3221_is_enabled(ina, channel)) { 374efb0489eSNicolin Chen /* Return 0 for alert flags */ 375efb0489eSNicolin Chen *val = 0; 376efb0489eSNicolin Chen return 0; 377efb0489eSNicolin Chen } 378d4b0166dSNicolin Chen ret = regmap_field_read(ina->fields[reg], ®val); 379d4b0166dSNicolin Chen if (ret) 380d4b0166dSNicolin Chen return ret; 381d4b0166dSNicolin Chen *val = regval; 382d4b0166dSNicolin Chen return 0; 383d4b0166dSNicolin Chen default: 384d4b0166dSNicolin Chen return -EOPNOTSUPP; 385d4b0166dSNicolin Chen } 3867cb6dcffSAndrew F. Davis } 3877cb6dcffSAndrew F. Davis 3885c090abfSNicolin Chen static int ina3221_write_chip(struct device *dev, u32 attr, long val) 3895c090abfSNicolin Chen { 3905c090abfSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 3915c090abfSNicolin Chen int ret, idx; 392521c0b61SNicolin Chen u32 tmp; 3935c090abfSNicolin Chen 3945c090abfSNicolin Chen switch (attr) { 3955c090abfSNicolin Chen case hwmon_chip_samples: 3965c090abfSNicolin Chen idx = find_closest(val, ina3221_avg_samples, 3975c090abfSNicolin Chen ARRAY_SIZE(ina3221_avg_samples)); 3985c090abfSNicolin Chen 399521c0b61SNicolin Chen tmp = (ina->reg_config & ~INA3221_CONFIG_AVG_MASK) | 400521c0b61SNicolin Chen (idx << INA3221_CONFIG_AVG_SHIFT); 401521c0b61SNicolin Chen ret = regmap_write(ina->regmap, INA3221_CONFIG, tmp); 4025c090abfSNicolin Chen if (ret) 4035c090abfSNicolin Chen return ret; 4045c090abfSNicolin Chen 4055c090abfSNicolin Chen /* Update reg_config accordingly */ 406521c0b61SNicolin Chen ina->reg_config = tmp; 407521c0b61SNicolin Chen return 0; 408023912dbSNicolin Chen case hwmon_chip_update_interval: 409023912dbSNicolin Chen tmp = ina3221_interval_ms_to_conv_time(ina->reg_config, val); 410023912dbSNicolin Chen idx = find_closest(tmp, ina3221_conv_time, 411023912dbSNicolin Chen ARRAY_SIZE(ina3221_conv_time)); 412023912dbSNicolin Chen 413023912dbSNicolin Chen /* Update Bus and Shunt voltage conversion times */ 414023912dbSNicolin Chen tmp = INA3221_CONFIG_VBUS_CT_MASK | INA3221_CONFIG_VSH_CT_MASK; 415023912dbSNicolin Chen tmp = (ina->reg_config & ~tmp) | 416023912dbSNicolin Chen (idx << INA3221_CONFIG_VBUS_CT_SHIFT) | 417023912dbSNicolin Chen (idx << INA3221_CONFIG_VSH_CT_SHIFT); 418023912dbSNicolin Chen ret = regmap_write(ina->regmap, INA3221_CONFIG, tmp); 419023912dbSNicolin Chen if (ret) 420023912dbSNicolin Chen return ret; 421023912dbSNicolin Chen 422023912dbSNicolin Chen /* Update reg_config accordingly */ 423023912dbSNicolin Chen ina->reg_config = tmp; 424023912dbSNicolin Chen return 0; 4255c090abfSNicolin Chen default: 4265c090abfSNicolin Chen return -EOPNOTSUPP; 4275c090abfSNicolin Chen } 4285c090abfSNicolin Chen } 4295c090abfSNicolin Chen 430d4b0166dSNicolin Chen static int ina3221_write_curr(struct device *dev, u32 attr, 431d4b0166dSNicolin Chen int channel, long val) 4327cb6dcffSAndrew F. Davis { 4337cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 4342057bdfbSNicolin Chen struct ina3221_input *input = ina->inputs; 435d4b0166dSNicolin Chen u8 reg = ina3221_curr_reg[attr][channel]; 4362057bdfbSNicolin Chen int resistance_uo, current_ma, voltage_uv; 4372057bdfbSNicolin Chen int regval; 4382057bdfbSNicolin Chen 4392057bdfbSNicolin Chen if (channel > INA3221_CHANNEL3) 4402057bdfbSNicolin Chen resistance_uo = ina->summation_shunt_resistor; 4412057bdfbSNicolin Chen else 4422057bdfbSNicolin Chen resistance_uo = input[channel].shunt_resistor; 4432057bdfbSNicolin Chen 4442057bdfbSNicolin Chen if (!resistance_uo) 4452057bdfbSNicolin Chen return -EOPNOTSUPP; 4467cb6dcffSAndrew F. Davis 4477cb6dcffSAndrew F. Davis /* clamp current */ 448d4b0166dSNicolin Chen current_ma = clamp_val(val, 4497cb6dcffSAndrew F. Davis INT_MIN / resistance_uo, 4507cb6dcffSAndrew F. Davis INT_MAX / resistance_uo); 4517cb6dcffSAndrew F. Davis 4527cb6dcffSAndrew F. Davis voltage_uv = DIV_ROUND_CLOSEST(current_ma * resistance_uo, 1000); 4537cb6dcffSAndrew F. Davis 4547cb6dcffSAndrew F. Davis /* clamp voltage */ 4557cb6dcffSAndrew F. Davis voltage_uv = clamp_val(voltage_uv, -163800, 163800); 4567cb6dcffSAndrew F. Davis 4572057bdfbSNicolin Chen /* 4582057bdfbSNicolin Chen * Formula to convert voltage_uv to register value: 4592057bdfbSNicolin Chen * regval = (voltage_uv / scale) << shift 4602057bdfbSNicolin Chen * Note: 4612057bdfbSNicolin Chen * The scale is 40uV for all shunt voltage registers 4622057bdfbSNicolin Chen * Shunt Voltage Sum register left-shifts 1 bit 4632057bdfbSNicolin Chen * All other Shunt Voltage registers shift 3 bits 4642057bdfbSNicolin Chen * Results: 4652057bdfbSNicolin Chen * SHUNT_SUM: (1 / 40uV) << 1 = 1 / 20uV 4662057bdfbSNicolin Chen * SHUNT[1-3]: (1 / 40uV) << 3 = 1 / 5uV 4672057bdfbSNicolin Chen */ 4682057bdfbSNicolin Chen if (reg == INA3221_SHUNT_SUM) 4692057bdfbSNicolin Chen regval = DIV_ROUND_CLOSEST(voltage_uv, 20) & 0xfffe; 4702057bdfbSNicolin Chen else 471d4b0166dSNicolin Chen regval = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8; 4727cb6dcffSAndrew F. Davis 473d4b0166dSNicolin Chen return regmap_write(ina->regmap, reg, regval); 474d4b0166dSNicolin Chen } 475d4b0166dSNicolin Chen 476d4b0166dSNicolin Chen static int ina3221_write_enable(struct device *dev, int channel, bool enable) 477d4b0166dSNicolin Chen { 478d4b0166dSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 479d4b0166dSNicolin Chen u16 config, mask = INA3221_CONFIG_CHx_EN(channel); 480323aeb0eSNicolin Chen u16 config_old = ina->reg_config & mask; 481521c0b61SNicolin Chen u32 tmp; 482d4b0166dSNicolin Chen int ret; 483d4b0166dSNicolin Chen 484d4b0166dSNicolin Chen config = enable ? mask : 0; 485d4b0166dSNicolin Chen 486323aeb0eSNicolin Chen /* Bypass if enable status is not being changed */ 487323aeb0eSNicolin Chen if (config_old == config) 488323aeb0eSNicolin Chen return 0; 489323aeb0eSNicolin Chen 490323aeb0eSNicolin Chen /* For enabling routine, increase refcount and resume() at first */ 491323aeb0eSNicolin Chen if (enable) { 492323aeb0eSNicolin Chen ret = pm_runtime_get_sync(ina->pm_dev); 493323aeb0eSNicolin Chen if (ret < 0) { 494323aeb0eSNicolin Chen dev_err(dev, "Failed to get PM runtime\n"); 495323aeb0eSNicolin Chen return ret; 496323aeb0eSNicolin Chen } 497323aeb0eSNicolin Chen } 498323aeb0eSNicolin Chen 499d4b0166dSNicolin Chen /* Enable or disable the channel */ 500521c0b61SNicolin Chen tmp = (ina->reg_config & ~mask) | (config & mask); 501521c0b61SNicolin Chen ret = regmap_write(ina->regmap, INA3221_CONFIG, tmp); 5027cb6dcffSAndrew F. Davis if (ret) 503323aeb0eSNicolin Chen goto fail; 5047cb6dcffSAndrew F. Davis 505d4b0166dSNicolin Chen /* Cache the latest config register value */ 506521c0b61SNicolin Chen ina->reg_config = tmp; 507323aeb0eSNicolin Chen 508323aeb0eSNicolin Chen /* For disabling routine, decrease refcount or suspend() at last */ 509323aeb0eSNicolin Chen if (!enable) 510323aeb0eSNicolin Chen pm_runtime_put_sync(ina->pm_dev); 511d4b0166dSNicolin Chen 512d4b0166dSNicolin Chen return 0; 513323aeb0eSNicolin Chen 514323aeb0eSNicolin Chen fail: 515323aeb0eSNicolin Chen if (enable) { 516323aeb0eSNicolin Chen dev_err(dev, "Failed to enable channel %d: error %d\n", 517323aeb0eSNicolin Chen channel, ret); 518323aeb0eSNicolin Chen pm_runtime_put_sync(ina->pm_dev); 519323aeb0eSNicolin Chen } 520323aeb0eSNicolin Chen 521323aeb0eSNicolin Chen return ret; 5227cb6dcffSAndrew F. Davis } 5237cb6dcffSAndrew F. Davis 524d4b0166dSNicolin Chen static int ina3221_read(struct device *dev, enum hwmon_sensor_types type, 525d4b0166dSNicolin Chen u32 attr, int channel, long *val) 526d4b0166dSNicolin Chen { 52787625b24SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 52887625b24SNicolin Chen int ret; 52987625b24SNicolin Chen 53087625b24SNicolin Chen mutex_lock(&ina->lock); 53187625b24SNicolin Chen 532d4b0166dSNicolin Chen switch (type) { 5335c090abfSNicolin Chen case hwmon_chip: 5345c090abfSNicolin Chen ret = ina3221_read_chip(dev, attr, val); 5355c090abfSNicolin Chen break; 536d4b0166dSNicolin Chen case hwmon_in: 537d4b0166dSNicolin Chen /* 0-align channel ID */ 53887625b24SNicolin Chen ret = ina3221_read_in(dev, attr, channel - 1, val); 53987625b24SNicolin Chen break; 540d4b0166dSNicolin Chen case hwmon_curr: 54187625b24SNicolin Chen ret = ina3221_read_curr(dev, attr, channel, val); 54287625b24SNicolin Chen break; 543d4b0166dSNicolin Chen default: 54487625b24SNicolin Chen ret = -EOPNOTSUPP; 54587625b24SNicolin Chen break; 546d4b0166dSNicolin Chen } 54787625b24SNicolin Chen 54887625b24SNicolin Chen mutex_unlock(&ina->lock); 54987625b24SNicolin Chen 55087625b24SNicolin Chen return ret; 551d4b0166dSNicolin Chen } 552d4b0166dSNicolin Chen 553d4b0166dSNicolin Chen static int ina3221_write(struct device *dev, enum hwmon_sensor_types type, 554d4b0166dSNicolin Chen u32 attr, int channel, long val) 555d4b0166dSNicolin Chen { 55687625b24SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 55787625b24SNicolin Chen int ret; 55887625b24SNicolin Chen 55987625b24SNicolin Chen mutex_lock(&ina->lock); 56087625b24SNicolin Chen 561d4b0166dSNicolin Chen switch (type) { 5625c090abfSNicolin Chen case hwmon_chip: 5635c090abfSNicolin Chen ret = ina3221_write_chip(dev, attr, val); 5645c090abfSNicolin Chen break; 565d4b0166dSNicolin Chen case hwmon_in: 566d4b0166dSNicolin Chen /* 0-align channel ID */ 56787625b24SNicolin Chen ret = ina3221_write_enable(dev, channel - 1, val); 56887625b24SNicolin Chen break; 569d4b0166dSNicolin Chen case hwmon_curr: 57087625b24SNicolin Chen ret = ina3221_write_curr(dev, attr, channel, val); 57187625b24SNicolin Chen break; 572d4b0166dSNicolin Chen default: 57387625b24SNicolin Chen ret = -EOPNOTSUPP; 57487625b24SNicolin Chen break; 575d4b0166dSNicolin Chen } 57687625b24SNicolin Chen 57787625b24SNicolin Chen mutex_unlock(&ina->lock); 57887625b24SNicolin Chen 57987625b24SNicolin Chen return ret; 580d4b0166dSNicolin Chen } 581d4b0166dSNicolin Chen 582d4b0166dSNicolin Chen static int ina3221_read_string(struct device *dev, enum hwmon_sensor_types type, 583d4b0166dSNicolin Chen u32 attr, int channel, const char **str) 584d4b0166dSNicolin Chen { 585d4b0166dSNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 586d4b0166dSNicolin Chen int index = channel - 1; 587d4b0166dSNicolin Chen 5882057bdfbSNicolin Chen if (channel == 7) 5892057bdfbSNicolin Chen *str = "sum of shunt voltages"; 5902057bdfbSNicolin Chen else 591d4b0166dSNicolin Chen *str = ina->inputs[index].label; 592d4b0166dSNicolin Chen 593d4b0166dSNicolin Chen return 0; 594d4b0166dSNicolin Chen } 595d4b0166dSNicolin Chen 596d4b0166dSNicolin Chen static umode_t ina3221_is_visible(const void *drvdata, 597d4b0166dSNicolin Chen enum hwmon_sensor_types type, 598d4b0166dSNicolin Chen u32 attr, int channel) 599d4b0166dSNicolin Chen { 600d4b0166dSNicolin Chen const struct ina3221_data *ina = drvdata; 601d4b0166dSNicolin Chen const struct ina3221_input *input = NULL; 602d4b0166dSNicolin Chen 603d4b0166dSNicolin Chen switch (type) { 6045c090abfSNicolin Chen case hwmon_chip: 6055c090abfSNicolin Chen switch (attr) { 6065c090abfSNicolin Chen case hwmon_chip_samples: 607023912dbSNicolin Chen case hwmon_chip_update_interval: 6085c090abfSNicolin Chen return 0644; 6095c090abfSNicolin Chen default: 6105c090abfSNicolin Chen return 0; 6115c090abfSNicolin Chen } 612d4b0166dSNicolin Chen case hwmon_in: 613d4b0166dSNicolin Chen /* Ignore in0_ */ 614d4b0166dSNicolin Chen if (channel == 0) 615d4b0166dSNicolin Chen return 0; 616d4b0166dSNicolin Chen 617d4b0166dSNicolin Chen switch (attr) { 618d4b0166dSNicolin Chen case hwmon_in_label: 619d4b0166dSNicolin Chen if (channel - 1 <= INA3221_CHANNEL3) 620d4b0166dSNicolin Chen input = &ina->inputs[channel - 1]; 6212057bdfbSNicolin Chen else if (channel == 7) 6222057bdfbSNicolin Chen return 0444; 623d4b0166dSNicolin Chen /* Hide label node if label is not provided */ 624d4b0166dSNicolin Chen return (input && input->label) ? 0444 : 0; 625d4b0166dSNicolin Chen case hwmon_in_input: 626d4b0166dSNicolin Chen return 0444; 627d4b0166dSNicolin Chen case hwmon_in_enable: 628d4b0166dSNicolin Chen return 0644; 629d4b0166dSNicolin Chen default: 630d4b0166dSNicolin Chen return 0; 631d4b0166dSNicolin Chen } 632d4b0166dSNicolin Chen case hwmon_curr: 633d4b0166dSNicolin Chen switch (attr) { 634d4b0166dSNicolin Chen case hwmon_curr_input: 635d4b0166dSNicolin Chen case hwmon_curr_crit_alarm: 636d4b0166dSNicolin Chen case hwmon_curr_max_alarm: 637d4b0166dSNicolin Chen return 0444; 638d4b0166dSNicolin Chen case hwmon_curr_crit: 639d4b0166dSNicolin Chen case hwmon_curr_max: 640d4b0166dSNicolin Chen return 0644; 641d4b0166dSNicolin Chen default: 642d4b0166dSNicolin Chen return 0; 643d4b0166dSNicolin Chen } 644d4b0166dSNicolin Chen default: 645d4b0166dSNicolin Chen return 0; 646d4b0166dSNicolin Chen } 647d4b0166dSNicolin Chen } 648d4b0166dSNicolin Chen 6496f307b7cSGuenter Roeck #define INA3221_HWMON_CURR_CONFIG (HWMON_C_INPUT | \ 6506f307b7cSGuenter Roeck HWMON_C_CRIT | HWMON_C_CRIT_ALARM | \ 6516f307b7cSGuenter Roeck HWMON_C_MAX | HWMON_C_MAX_ALARM) 6526f307b7cSGuenter Roeck 6536f307b7cSGuenter Roeck static const struct hwmon_channel_info *ina3221_info[] = { 6545c090abfSNicolin Chen HWMON_CHANNEL_INFO(chip, 655023912dbSNicolin Chen HWMON_C_SAMPLES, 656023912dbSNicolin Chen HWMON_C_UPDATE_INTERVAL), 6576f307b7cSGuenter Roeck HWMON_CHANNEL_INFO(in, 658d4b0166dSNicolin Chen /* 0: dummy, skipped in is_visible */ 659d4b0166dSNicolin Chen HWMON_I_INPUT, 660d4b0166dSNicolin Chen /* 1-3: input voltage Channels */ 661d4b0166dSNicolin Chen HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, 662d4b0166dSNicolin Chen HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, 663d4b0166dSNicolin Chen HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, 664d4b0166dSNicolin Chen /* 4-6: shunt voltage Channels */ 665d4b0166dSNicolin Chen HWMON_I_INPUT, 666d4b0166dSNicolin Chen HWMON_I_INPUT, 6672057bdfbSNicolin Chen HWMON_I_INPUT, 6682057bdfbSNicolin Chen /* 7: summation of shunt voltage channels */ 6692057bdfbSNicolin Chen HWMON_I_INPUT | HWMON_I_LABEL), 6706f307b7cSGuenter Roeck HWMON_CHANNEL_INFO(curr, 6712057bdfbSNicolin Chen /* 1-3: current channels*/ 672d4b0166dSNicolin Chen INA3221_HWMON_CURR_CONFIG, 673d4b0166dSNicolin Chen INA3221_HWMON_CURR_CONFIG, 6742057bdfbSNicolin Chen INA3221_HWMON_CURR_CONFIG, 6752057bdfbSNicolin Chen /* 4: summation of current channels */ 6762057bdfbSNicolin Chen HWMON_C_INPUT | HWMON_C_CRIT | HWMON_C_CRIT_ALARM), 677d4b0166dSNicolin Chen NULL 678d4b0166dSNicolin Chen }; 679d4b0166dSNicolin Chen 680d4b0166dSNicolin Chen static const struct hwmon_ops ina3221_hwmon_ops = { 681d4b0166dSNicolin Chen .is_visible = ina3221_is_visible, 682d4b0166dSNicolin Chen .read_string = ina3221_read_string, 683d4b0166dSNicolin Chen .read = ina3221_read, 684d4b0166dSNicolin Chen .write = ina3221_write, 685d4b0166dSNicolin Chen }; 686d4b0166dSNicolin Chen 687d4b0166dSNicolin Chen static const struct hwmon_chip_info ina3221_chip_info = { 688d4b0166dSNicolin Chen .ops = &ina3221_hwmon_ops, 689d4b0166dSNicolin Chen .info = ina3221_info, 690d4b0166dSNicolin Chen }; 691d4b0166dSNicolin Chen 692d4b0166dSNicolin Chen /* Extra attribute groups */ 693a4ec92edSGuenter Roeck static ssize_t ina3221_shunt_show(struct device *dev, 6947cb6dcffSAndrew F. Davis struct device_attribute *attr, char *buf) 6957cb6dcffSAndrew F. Davis { 6967cb6dcffSAndrew F. Davis struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); 6977cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 6987cb6dcffSAndrew F. Davis unsigned int channel = sd_attr->index; 699a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 7007cb6dcffSAndrew F. Davis 701a9e9dd9cSNicolin Chen return snprintf(buf, PAGE_SIZE, "%d\n", input->shunt_resistor); 7027cb6dcffSAndrew F. Davis } 7037cb6dcffSAndrew F. Davis 704a4ec92edSGuenter Roeck static ssize_t ina3221_shunt_store(struct device *dev, 7057cb6dcffSAndrew F. Davis struct device_attribute *attr, 7067cb6dcffSAndrew F. Davis const char *buf, size_t count) 7077cb6dcffSAndrew F. Davis { 7087cb6dcffSAndrew F. Davis struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); 7097cb6dcffSAndrew F. Davis struct ina3221_data *ina = dev_get_drvdata(dev); 7107cb6dcffSAndrew F. Davis unsigned int channel = sd_attr->index; 711a9e9dd9cSNicolin Chen struct ina3221_input *input = &ina->inputs[channel]; 7129ad0df1aSGuenter Roeck int val; 7137cb6dcffSAndrew F. Davis int ret; 7147cb6dcffSAndrew F. Davis 7159ad0df1aSGuenter Roeck ret = kstrtoint(buf, 0, &val); 7167cb6dcffSAndrew F. Davis if (ret) 7177cb6dcffSAndrew F. Davis return ret; 7187cb6dcffSAndrew F. Davis 7199ad0df1aSGuenter Roeck val = clamp_val(val, 1, INT_MAX); 7207cb6dcffSAndrew F. Davis 721a9e9dd9cSNicolin Chen input->shunt_resistor = val; 7227cb6dcffSAndrew F. Davis 7232057bdfbSNicolin Chen /* Update summation_shunt_resistor for summation channel */ 7242057bdfbSNicolin Chen ina->summation_shunt_resistor = ina3221_summation_shunt_resistor(ina); 7252057bdfbSNicolin Chen 7267cb6dcffSAndrew F. Davis return count; 7277cb6dcffSAndrew F. Davis } 7287cb6dcffSAndrew F. Davis 7297cb6dcffSAndrew F. Davis /* shunt resistance */ 730a4ec92edSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(shunt1_resistor, ina3221_shunt, INA3221_CHANNEL1); 731a4ec92edSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(shunt2_resistor, ina3221_shunt, INA3221_CHANNEL2); 732a4ec92edSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(shunt3_resistor, ina3221_shunt, INA3221_CHANNEL3); 7337cb6dcffSAndrew F. Davis 7347cb6dcffSAndrew F. Davis static struct attribute *ina3221_attrs[] = { 7357cb6dcffSAndrew F. Davis &sensor_dev_attr_shunt1_resistor.dev_attr.attr, 7367cb6dcffSAndrew F. Davis &sensor_dev_attr_shunt2_resistor.dev_attr.attr, 7377cb6dcffSAndrew F. Davis &sensor_dev_attr_shunt3_resistor.dev_attr.attr, 7387cb6dcffSAndrew F. Davis NULL, 7397cb6dcffSAndrew F. Davis }; 740d4b0166dSNicolin Chen ATTRIBUTE_GROUPS(ina3221); 7417cb6dcffSAndrew F. Davis 7427cb6dcffSAndrew F. Davis static const struct regmap_range ina3221_yes_ranges[] = { 743c20217b3SNicolin Chen regmap_reg_range(INA3221_CONFIG, INA3221_BUS3), 7442057bdfbSNicolin Chen regmap_reg_range(INA3221_SHUNT_SUM, INA3221_SHUNT_SUM), 7457cb6dcffSAndrew F. Davis regmap_reg_range(INA3221_MASK_ENABLE, INA3221_MASK_ENABLE), 7467cb6dcffSAndrew F. Davis }; 7477cb6dcffSAndrew F. Davis 7487cb6dcffSAndrew F. Davis static const struct regmap_access_table ina3221_volatile_table = { 7497cb6dcffSAndrew F. Davis .yes_ranges = ina3221_yes_ranges, 7507cb6dcffSAndrew F. Davis .n_yes_ranges = ARRAY_SIZE(ina3221_yes_ranges), 7517cb6dcffSAndrew F. Davis }; 7527cb6dcffSAndrew F. Davis 7537cb6dcffSAndrew F. Davis static const struct regmap_config ina3221_regmap_config = { 7547cb6dcffSAndrew F. Davis .reg_bits = 8, 7557cb6dcffSAndrew F. Davis .val_bits = 16, 7567cb6dcffSAndrew F. Davis 7577cb6dcffSAndrew F. Davis .cache_type = REGCACHE_RBTREE, 7587cb6dcffSAndrew F. Davis .volatile_table = &ina3221_volatile_table, 7597cb6dcffSAndrew F. Davis }; 7607cb6dcffSAndrew F. Davis 761a9e9dd9cSNicolin Chen static int ina3221_probe_child_from_dt(struct device *dev, 762a9e9dd9cSNicolin Chen struct device_node *child, 763a9e9dd9cSNicolin Chen struct ina3221_data *ina) 764a9e9dd9cSNicolin Chen { 765a9e9dd9cSNicolin Chen struct ina3221_input *input; 766a9e9dd9cSNicolin Chen u32 val; 767a9e9dd9cSNicolin Chen int ret; 768a9e9dd9cSNicolin Chen 769a9e9dd9cSNicolin Chen ret = of_property_read_u32(child, "reg", &val); 770a9e9dd9cSNicolin Chen if (ret) { 7711b1f4efaSRob Herring dev_err(dev, "missing reg property of %pOFn\n", child); 772a9e9dd9cSNicolin Chen return ret; 773a9e9dd9cSNicolin Chen } else if (val > INA3221_CHANNEL3) { 7741b1f4efaSRob Herring dev_err(dev, "invalid reg %d of %pOFn\n", val, child); 775a9e9dd9cSNicolin Chen return ret; 776a9e9dd9cSNicolin Chen } 777a9e9dd9cSNicolin Chen 778a9e9dd9cSNicolin Chen input = &ina->inputs[val]; 779a9e9dd9cSNicolin Chen 780a9e9dd9cSNicolin Chen /* Log the disconnected channel input */ 781a9e9dd9cSNicolin Chen if (!of_device_is_available(child)) { 782a9e9dd9cSNicolin Chen input->disconnected = true; 783a9e9dd9cSNicolin Chen return 0; 784a9e9dd9cSNicolin Chen } 785a9e9dd9cSNicolin Chen 786a9e9dd9cSNicolin Chen /* Save the connected input label if available */ 787a9e9dd9cSNicolin Chen of_property_read_string(child, "label", &input->label); 788a9e9dd9cSNicolin Chen 789a9e9dd9cSNicolin Chen /* Overwrite default shunt resistor value optionally */ 790a6e43263SNicolin Chen if (!of_property_read_u32(child, "shunt-resistor-micro-ohms", &val)) { 791a6e43263SNicolin Chen if (val < 1 || val > INT_MAX) { 7921b1f4efaSRob Herring dev_err(dev, "invalid shunt resistor value %u of %pOFn\n", 7931b1f4efaSRob Herring val, child); 794a6e43263SNicolin Chen return -EINVAL; 795a6e43263SNicolin Chen } 796a9e9dd9cSNicolin Chen input->shunt_resistor = val; 797a6e43263SNicolin Chen } 798a9e9dd9cSNicolin Chen 799a9e9dd9cSNicolin Chen return 0; 800a9e9dd9cSNicolin Chen } 801a9e9dd9cSNicolin Chen 802a9e9dd9cSNicolin Chen static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina) 803a9e9dd9cSNicolin Chen { 804a9e9dd9cSNicolin Chen const struct device_node *np = dev->of_node; 805a9e9dd9cSNicolin Chen struct device_node *child; 806a9e9dd9cSNicolin Chen int ret; 807a9e9dd9cSNicolin Chen 808a9e9dd9cSNicolin Chen /* Compatible with non-DT platforms */ 809a9e9dd9cSNicolin Chen if (!np) 810a9e9dd9cSNicolin Chen return 0; 811a9e9dd9cSNicolin Chen 81243dece16SNicolin Chen ina->single_shot = of_property_read_bool(np, "ti,single-shot"); 81343dece16SNicolin Chen 814a9e9dd9cSNicolin Chen for_each_child_of_node(np, child) { 815a9e9dd9cSNicolin Chen ret = ina3221_probe_child_from_dt(dev, child, ina); 8169f754657SNishka Dasgupta if (ret) { 8179f754657SNishka Dasgupta of_node_put(child); 818a9e9dd9cSNicolin Chen return ret; 819a9e9dd9cSNicolin Chen } 8209f754657SNishka Dasgupta } 821a9e9dd9cSNicolin Chen 822a9e9dd9cSNicolin Chen return 0; 823a9e9dd9cSNicolin Chen } 824a9e9dd9cSNicolin Chen 8257cb6dcffSAndrew F. Davis static int ina3221_probe(struct i2c_client *client, 8267cb6dcffSAndrew F. Davis const struct i2c_device_id *id) 8277cb6dcffSAndrew F. Davis { 8287cb6dcffSAndrew F. Davis struct device *dev = &client->dev; 8297cb6dcffSAndrew F. Davis struct ina3221_data *ina; 8307cb6dcffSAndrew F. Davis struct device *hwmon_dev; 8317cb6dcffSAndrew F. Davis int i, ret; 8327cb6dcffSAndrew F. Davis 8337cb6dcffSAndrew F. Davis ina = devm_kzalloc(dev, sizeof(*ina), GFP_KERNEL); 8347cb6dcffSAndrew F. Davis if (!ina) 8357cb6dcffSAndrew F. Davis return -ENOMEM; 8367cb6dcffSAndrew F. Davis 8377cb6dcffSAndrew F. Davis ina->regmap = devm_regmap_init_i2c(client, &ina3221_regmap_config); 8387cb6dcffSAndrew F. Davis if (IS_ERR(ina->regmap)) { 8397cb6dcffSAndrew F. Davis dev_err(dev, "Unable to allocate register map\n"); 8407cb6dcffSAndrew F. Davis return PTR_ERR(ina->regmap); 8417cb6dcffSAndrew F. Davis } 8427cb6dcffSAndrew F. Davis 8437cb6dcffSAndrew F. Davis for (i = 0; i < F_MAX_FIELDS; i++) { 8447cb6dcffSAndrew F. Davis ina->fields[i] = devm_regmap_field_alloc(dev, 8457cb6dcffSAndrew F. Davis ina->regmap, 8467cb6dcffSAndrew F. Davis ina3221_reg_fields[i]); 8477cb6dcffSAndrew F. Davis if (IS_ERR(ina->fields[i])) { 8487cb6dcffSAndrew F. Davis dev_err(dev, "Unable to allocate regmap fields\n"); 8497cb6dcffSAndrew F. Davis return PTR_ERR(ina->fields[i]); 8507cb6dcffSAndrew F. Davis } 8517cb6dcffSAndrew F. Davis } 8527cb6dcffSAndrew F. Davis 8537cb6dcffSAndrew F. Davis for (i = 0; i < INA3221_NUM_CHANNELS; i++) 854a9e9dd9cSNicolin Chen ina->inputs[i].shunt_resistor = INA3221_RSHUNT_DEFAULT; 855a9e9dd9cSNicolin Chen 856a9e9dd9cSNicolin Chen ret = ina3221_probe_from_dt(dev, ina); 857a9e9dd9cSNicolin Chen if (ret) { 858a9e9dd9cSNicolin Chen dev_err(dev, "Unable to probe from device tree\n"); 859a9e9dd9cSNicolin Chen return ret; 860a9e9dd9cSNicolin Chen } 8617cb6dcffSAndrew F. Davis 862323aeb0eSNicolin Chen /* The driver will be reset, so use reset value */ 863323aeb0eSNicolin Chen ina->reg_config = INA3221_CONFIG_DEFAULT; 864a9e9dd9cSNicolin Chen 86543dece16SNicolin Chen /* Clear continuous bit to use single-shot mode */ 86643dece16SNicolin Chen if (ina->single_shot) 86743dece16SNicolin Chen ina->reg_config &= ~INA3221_CONFIG_MODE_CONTINUOUS; 86843dece16SNicolin Chen 869a9e9dd9cSNicolin Chen /* Disable channels if their inputs are disconnected */ 870a9e9dd9cSNicolin Chen for (i = 0; i < INA3221_NUM_CHANNELS; i++) { 871a9e9dd9cSNicolin Chen if (ina->inputs[i].disconnected) 872a9e9dd9cSNicolin Chen ina->reg_config &= ~INA3221_CONFIG_CHx_EN(i); 873a9e9dd9cSNicolin Chen } 874a9e9dd9cSNicolin Chen 8752057bdfbSNicolin Chen /* Initialize summation_shunt_resistor for summation channel control */ 8762057bdfbSNicolin Chen ina->summation_shunt_resistor = ina3221_summation_shunt_resistor(ina); 8772057bdfbSNicolin Chen 878323aeb0eSNicolin Chen ina->pm_dev = dev; 87987625b24SNicolin Chen mutex_init(&ina->lock); 88059d608e1SNicolin Chen dev_set_drvdata(dev, ina); 88159d608e1SNicolin Chen 882323aeb0eSNicolin Chen /* Enable PM runtime -- status is suspended by default */ 883323aeb0eSNicolin Chen pm_runtime_enable(ina->pm_dev); 884323aeb0eSNicolin Chen 885323aeb0eSNicolin Chen /* Initialize (resume) the device */ 886323aeb0eSNicolin Chen for (i = 0; i < INA3221_NUM_CHANNELS; i++) { 887323aeb0eSNicolin Chen if (ina->inputs[i].disconnected) 888323aeb0eSNicolin Chen continue; 889323aeb0eSNicolin Chen /* Match the refcount with number of enabled channels */ 890323aeb0eSNicolin Chen ret = pm_runtime_get_sync(ina->pm_dev); 891323aeb0eSNicolin Chen if (ret < 0) 892323aeb0eSNicolin Chen goto fail; 893323aeb0eSNicolin Chen } 894323aeb0eSNicolin Chen 895d4b0166dSNicolin Chen hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, ina, 896d4b0166dSNicolin Chen &ina3221_chip_info, 897d4b0166dSNicolin Chen ina3221_groups); 8987cb6dcffSAndrew F. Davis if (IS_ERR(hwmon_dev)) { 8997cb6dcffSAndrew F. Davis dev_err(dev, "Unable to register hwmon device\n"); 900323aeb0eSNicolin Chen ret = PTR_ERR(hwmon_dev); 901323aeb0eSNicolin Chen goto fail; 9027cb6dcffSAndrew F. Davis } 9037cb6dcffSAndrew F. Davis 9047cb6dcffSAndrew F. Davis return 0; 905323aeb0eSNicolin Chen 906323aeb0eSNicolin Chen fail: 907323aeb0eSNicolin Chen pm_runtime_disable(ina->pm_dev); 908323aeb0eSNicolin Chen pm_runtime_set_suspended(ina->pm_dev); 909323aeb0eSNicolin Chen /* pm_runtime_put_noidle() will decrease the PM refcount until 0 */ 910323aeb0eSNicolin Chen for (i = 0; i < INA3221_NUM_CHANNELS; i++) 911323aeb0eSNicolin Chen pm_runtime_put_noidle(ina->pm_dev); 912323aeb0eSNicolin Chen mutex_destroy(&ina->lock); 913323aeb0eSNicolin Chen 914323aeb0eSNicolin Chen return ret; 9157cb6dcffSAndrew F. Davis } 9167cb6dcffSAndrew F. Davis 91787625b24SNicolin Chen static int ina3221_remove(struct i2c_client *client) 91887625b24SNicolin Chen { 91987625b24SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(&client->dev); 920323aeb0eSNicolin Chen int i; 921323aeb0eSNicolin Chen 922323aeb0eSNicolin Chen pm_runtime_disable(ina->pm_dev); 923323aeb0eSNicolin Chen pm_runtime_set_suspended(ina->pm_dev); 924323aeb0eSNicolin Chen 925323aeb0eSNicolin Chen /* pm_runtime_put_noidle() will decrease the PM refcount until 0 */ 926323aeb0eSNicolin Chen for (i = 0; i < INA3221_NUM_CHANNELS; i++) 927323aeb0eSNicolin Chen pm_runtime_put_noidle(ina->pm_dev); 92887625b24SNicolin Chen 92987625b24SNicolin Chen mutex_destroy(&ina->lock); 93087625b24SNicolin Chen 93187625b24SNicolin Chen return 0; 93287625b24SNicolin Chen } 93387625b24SNicolin Chen 934ead21c77SArnd Bergmann static int __maybe_unused ina3221_suspend(struct device *dev) 93559d608e1SNicolin Chen { 93659d608e1SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 93759d608e1SNicolin Chen int ret; 93859d608e1SNicolin Chen 93959d608e1SNicolin Chen /* Save config register value and enable cache-only */ 94059d608e1SNicolin Chen ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config); 94159d608e1SNicolin Chen if (ret) 94259d608e1SNicolin Chen return ret; 94359d608e1SNicolin Chen 94459d608e1SNicolin Chen /* Set to power-down mode for power saving */ 94559d608e1SNicolin Chen ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, 94659d608e1SNicolin Chen INA3221_CONFIG_MODE_MASK, 94759d608e1SNicolin Chen INA3221_CONFIG_MODE_POWERDOWN); 94859d608e1SNicolin Chen if (ret) 94959d608e1SNicolin Chen return ret; 95059d608e1SNicolin Chen 95159d608e1SNicolin Chen regcache_cache_only(ina->regmap, true); 95259d608e1SNicolin Chen regcache_mark_dirty(ina->regmap); 95359d608e1SNicolin Chen 95459d608e1SNicolin Chen return 0; 95559d608e1SNicolin Chen } 95659d608e1SNicolin Chen 957ead21c77SArnd Bergmann static int __maybe_unused ina3221_resume(struct device *dev) 95859d608e1SNicolin Chen { 95959d608e1SNicolin Chen struct ina3221_data *ina = dev_get_drvdata(dev); 96059d608e1SNicolin Chen int ret; 96159d608e1SNicolin Chen 96259d608e1SNicolin Chen regcache_cache_only(ina->regmap, false); 96359d608e1SNicolin Chen 96459d608e1SNicolin Chen /* Software reset the chip */ 96559d608e1SNicolin Chen ret = regmap_field_write(ina->fields[F_RST], true); 96659d608e1SNicolin Chen if (ret) { 96759d608e1SNicolin Chen dev_err(dev, "Unable to reset device\n"); 96859d608e1SNicolin Chen return ret; 96959d608e1SNicolin Chen } 97059d608e1SNicolin Chen 97159d608e1SNicolin Chen /* Restore cached register values to hardware */ 97259d608e1SNicolin Chen ret = regcache_sync(ina->regmap); 97359d608e1SNicolin Chen if (ret) 97459d608e1SNicolin Chen return ret; 97559d608e1SNicolin Chen 97659d608e1SNicolin Chen /* Restore config register value to hardware */ 97759d608e1SNicolin Chen ret = regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config); 97859d608e1SNicolin Chen if (ret) 97959d608e1SNicolin Chen return ret; 98059d608e1SNicolin Chen 9812057bdfbSNicolin Chen /* Initialize summation channel control */ 9822057bdfbSNicolin Chen if (ina->summation_shunt_resistor) { 9832057bdfbSNicolin Chen /* 9842057bdfbSNicolin Chen * Take all three channels into summation by default 9852057bdfbSNicolin Chen * Shunt measurements of disconnected channels should 9862057bdfbSNicolin Chen * be 0, so it does not matter for summation. 9872057bdfbSNicolin Chen */ 9882057bdfbSNicolin Chen ret = regmap_update_bits(ina->regmap, INA3221_MASK_ENABLE, 9892057bdfbSNicolin Chen INA3221_MASK_ENABLE_SCC_MASK, 9902057bdfbSNicolin Chen INA3221_MASK_ENABLE_SCC_MASK); 9912057bdfbSNicolin Chen if (ret) { 9922057bdfbSNicolin Chen dev_err(dev, "Unable to control summation channel\n"); 9932057bdfbSNicolin Chen return ret; 9942057bdfbSNicolin Chen } 9952057bdfbSNicolin Chen } 9962057bdfbSNicolin Chen 99759d608e1SNicolin Chen return 0; 99859d608e1SNicolin Chen } 99959d608e1SNicolin Chen 100059d608e1SNicolin Chen static const struct dev_pm_ops ina3221_pm = { 1001323aeb0eSNicolin Chen SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 1002323aeb0eSNicolin Chen pm_runtime_force_resume) 1003323aeb0eSNicolin Chen SET_RUNTIME_PM_OPS(ina3221_suspend, ina3221_resume, NULL) 100459d608e1SNicolin Chen }; 100559d608e1SNicolin Chen 10067cb6dcffSAndrew F. Davis static const struct of_device_id ina3221_of_match_table[] = { 10077cb6dcffSAndrew F. Davis { .compatible = "ti,ina3221", }, 10087cb6dcffSAndrew F. Davis { /* sentinel */ } 10097cb6dcffSAndrew F. Davis }; 10107cb6dcffSAndrew F. Davis MODULE_DEVICE_TABLE(of, ina3221_of_match_table); 10117cb6dcffSAndrew F. Davis 10127cb6dcffSAndrew F. Davis static const struct i2c_device_id ina3221_ids[] = { 10137cb6dcffSAndrew F. Davis { "ina3221", 0 }, 10147cb6dcffSAndrew F. Davis { /* sentinel */ } 10157cb6dcffSAndrew F. Davis }; 10167cb6dcffSAndrew F. Davis MODULE_DEVICE_TABLE(i2c, ina3221_ids); 10177cb6dcffSAndrew F. Davis 10187cb6dcffSAndrew F. Davis static struct i2c_driver ina3221_i2c_driver = { 10197cb6dcffSAndrew F. Davis .probe = ina3221_probe, 102087625b24SNicolin Chen .remove = ina3221_remove, 10217cb6dcffSAndrew F. Davis .driver = { 10227cb6dcffSAndrew F. Davis .name = INA3221_DRIVER_NAME, 10237cb6dcffSAndrew F. Davis .of_match_table = ina3221_of_match_table, 102459d608e1SNicolin Chen .pm = &ina3221_pm, 10257cb6dcffSAndrew F. Davis }, 10267cb6dcffSAndrew F. Davis .id_table = ina3221_ids, 10277cb6dcffSAndrew F. Davis }; 10287cb6dcffSAndrew F. Davis module_i2c_driver(ina3221_i2c_driver); 10297cb6dcffSAndrew F. Davis 10307cb6dcffSAndrew F. Davis MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); 10317cb6dcffSAndrew F. Davis MODULE_DESCRIPTION("Texas Instruments INA3221 HWMon Driver"); 10327cb6dcffSAndrew F. Davis MODULE_LICENSE("GPL v2"); 1033