xref: /openbmc/linux/drivers/hwmon/ina3221.c (revision 791ebc9d)
17cb6dcffSAndrew F. Davis /*
27cb6dcffSAndrew F. Davis  * INA3221 Triple Current/Voltage Monitor
37cb6dcffSAndrew F. Davis  *
47cb6dcffSAndrew F. Davis  * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/
57cb6dcffSAndrew F. Davis  *	Andrew F. Davis <afd@ti.com>
67cb6dcffSAndrew F. Davis  *
77cb6dcffSAndrew F. Davis  * This program is free software; you can redistribute it and/or modify
87cb6dcffSAndrew F. Davis  * it under the terms of the GNU General Public License version 2 as
97cb6dcffSAndrew F. Davis  * published by the Free Software Foundation.
107cb6dcffSAndrew F. Davis  *
117cb6dcffSAndrew F. Davis  * This program is distributed in the hope that it will be useful, but
127cb6dcffSAndrew F. Davis  * WITHOUT ANY WARRANTY; without even the implied warranty of
137cb6dcffSAndrew F. Davis  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
147cb6dcffSAndrew F. Davis  * General Public License for more details.
157cb6dcffSAndrew F. Davis  */
167cb6dcffSAndrew F. Davis 
177cb6dcffSAndrew F. Davis #include <linux/hwmon.h>
187cb6dcffSAndrew F. Davis #include <linux/hwmon-sysfs.h>
197cb6dcffSAndrew F. Davis #include <linux/i2c.h>
207cb6dcffSAndrew F. Davis #include <linux/module.h>
217cb6dcffSAndrew F. Davis #include <linux/of.h>
227cb6dcffSAndrew F. Davis #include <linux/regmap.h>
237cb6dcffSAndrew F. Davis 
247cb6dcffSAndrew F. Davis #define INA3221_DRIVER_NAME		"ina3221"
257cb6dcffSAndrew F. Davis 
267cb6dcffSAndrew F. Davis #define INA3221_CONFIG			0x00
277cb6dcffSAndrew F. Davis #define INA3221_SHUNT1			0x01
287cb6dcffSAndrew F. Davis #define INA3221_BUS1			0x02
297cb6dcffSAndrew F. Davis #define INA3221_SHUNT2			0x03
307cb6dcffSAndrew F. Davis #define INA3221_BUS2			0x04
317cb6dcffSAndrew F. Davis #define INA3221_SHUNT3			0x05
327cb6dcffSAndrew F. Davis #define INA3221_BUS3			0x06
337cb6dcffSAndrew F. Davis #define INA3221_CRIT1			0x07
347cb6dcffSAndrew F. Davis #define INA3221_WARN1			0x08
357cb6dcffSAndrew F. Davis #define INA3221_CRIT2			0x09
367cb6dcffSAndrew F. Davis #define INA3221_WARN2			0x0a
377cb6dcffSAndrew F. Davis #define INA3221_CRIT3			0x0b
387cb6dcffSAndrew F. Davis #define INA3221_WARN3			0x0c
397cb6dcffSAndrew F. Davis #define INA3221_MASK_ENABLE		0x0f
407cb6dcffSAndrew F. Davis 
41791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_SHUNT	BIT(0)
42791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_BUS		BIT(1)
43791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_CONTINUOUS	BIT(2)
447cb6dcffSAndrew F. Davis 
457cb6dcffSAndrew F. Davis #define INA3221_RSHUNT_DEFAULT		10000
467cb6dcffSAndrew F. Davis 
477cb6dcffSAndrew F. Davis enum ina3221_fields {
487cb6dcffSAndrew F. Davis 	/* Configuration */
497cb6dcffSAndrew F. Davis 	F_RST,
507cb6dcffSAndrew F. Davis 
517cb6dcffSAndrew F. Davis 	/* Alert Flags */
527cb6dcffSAndrew F. Davis 	F_WF3, F_WF2, F_WF1,
537cb6dcffSAndrew F. Davis 	F_CF3, F_CF2, F_CF1,
547cb6dcffSAndrew F. Davis 
557cb6dcffSAndrew F. Davis 	/* sentinel */
567cb6dcffSAndrew F. Davis 	F_MAX_FIELDS
577cb6dcffSAndrew F. Davis };
587cb6dcffSAndrew F. Davis 
597cb6dcffSAndrew F. Davis static const struct reg_field ina3221_reg_fields[] = {
607cb6dcffSAndrew F. Davis 	[F_RST] = REG_FIELD(INA3221_CONFIG, 15, 15),
617cb6dcffSAndrew F. Davis 
627cb6dcffSAndrew F. Davis 	[F_WF3] = REG_FIELD(INA3221_MASK_ENABLE, 3, 3),
637cb6dcffSAndrew F. Davis 	[F_WF2] = REG_FIELD(INA3221_MASK_ENABLE, 4, 4),
647cb6dcffSAndrew F. Davis 	[F_WF1] = REG_FIELD(INA3221_MASK_ENABLE, 5, 5),
657cb6dcffSAndrew F. Davis 	[F_CF3] = REG_FIELD(INA3221_MASK_ENABLE, 7, 7),
667cb6dcffSAndrew F. Davis 	[F_CF2] = REG_FIELD(INA3221_MASK_ENABLE, 8, 8),
677cb6dcffSAndrew F. Davis 	[F_CF1] = REG_FIELD(INA3221_MASK_ENABLE, 9, 9),
687cb6dcffSAndrew F. Davis };
697cb6dcffSAndrew F. Davis 
707cb6dcffSAndrew F. Davis enum ina3221_channels {
717cb6dcffSAndrew F. Davis 	INA3221_CHANNEL1,
727cb6dcffSAndrew F. Davis 	INA3221_CHANNEL2,
737cb6dcffSAndrew F. Davis 	INA3221_CHANNEL3,
747cb6dcffSAndrew F. Davis 	INA3221_NUM_CHANNELS
757cb6dcffSAndrew F. Davis };
767cb6dcffSAndrew F. Davis 
777cb6dcffSAndrew F. Davis static const unsigned int register_channel[] = {
787cb6dcffSAndrew F. Davis 	[INA3221_SHUNT1] = INA3221_CHANNEL1,
797cb6dcffSAndrew F. Davis 	[INA3221_SHUNT2] = INA3221_CHANNEL2,
807cb6dcffSAndrew F. Davis 	[INA3221_SHUNT3] = INA3221_CHANNEL3,
817cb6dcffSAndrew F. Davis 	[INA3221_CRIT1] = INA3221_CHANNEL1,
827cb6dcffSAndrew F. Davis 	[INA3221_CRIT2] = INA3221_CHANNEL2,
837cb6dcffSAndrew F. Davis 	[INA3221_CRIT3] = INA3221_CHANNEL3,
847cb6dcffSAndrew F. Davis 	[INA3221_WARN1] = INA3221_CHANNEL1,
857cb6dcffSAndrew F. Davis 	[INA3221_WARN2] = INA3221_CHANNEL2,
867cb6dcffSAndrew F. Davis 	[INA3221_WARN3] = INA3221_CHANNEL3,
877cb6dcffSAndrew F. Davis };
887cb6dcffSAndrew F. Davis 
897cb6dcffSAndrew F. Davis /**
907cb6dcffSAndrew F. Davis  * struct ina3221_data - device specific information
917cb6dcffSAndrew F. Davis  * @regmap: Register map of the device
927cb6dcffSAndrew F. Davis  * @fields: Register fields of the device
937cb6dcffSAndrew F. Davis  * @shunt_resistors: Array of resistor values per channel
947cb6dcffSAndrew F. Davis  */
957cb6dcffSAndrew F. Davis struct ina3221_data {
967cb6dcffSAndrew F. Davis 	struct regmap *regmap;
977cb6dcffSAndrew F. Davis 	struct regmap_field *fields[F_MAX_FIELDS];
989ad0df1aSGuenter Roeck 	int shunt_resistors[INA3221_NUM_CHANNELS];
997cb6dcffSAndrew F. Davis };
1007cb6dcffSAndrew F. Davis 
1017cb6dcffSAndrew F. Davis static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg,
1027cb6dcffSAndrew F. Davis 			      int *val)
1037cb6dcffSAndrew F. Davis {
1047cb6dcffSAndrew F. Davis 	unsigned int regval;
1057cb6dcffSAndrew F. Davis 	int ret;
1067cb6dcffSAndrew F. Davis 
1077cb6dcffSAndrew F. Davis 	ret = regmap_read(ina->regmap, reg, &regval);
1087cb6dcffSAndrew F. Davis 	if (ret)
1097cb6dcffSAndrew F. Davis 		return ret;
1107cb6dcffSAndrew F. Davis 
1117cb6dcffSAndrew F. Davis 	*val = sign_extend32(regval >> 3, 12);
1127cb6dcffSAndrew F. Davis 
1137cb6dcffSAndrew F. Davis 	return 0;
1147cb6dcffSAndrew F. Davis }
1157cb6dcffSAndrew F. Davis 
1167cb6dcffSAndrew F. Davis static ssize_t ina3221_show_bus_voltage(struct device *dev,
1177cb6dcffSAndrew F. Davis 					struct device_attribute *attr,
1187cb6dcffSAndrew F. Davis 					char *buf)
1197cb6dcffSAndrew F. Davis {
1207cb6dcffSAndrew F. Davis 	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
1217cb6dcffSAndrew F. Davis 	struct ina3221_data *ina = dev_get_drvdata(dev);
1227cb6dcffSAndrew F. Davis 	unsigned int reg = sd_attr->index;
1237cb6dcffSAndrew F. Davis 	int val, voltage_mv, ret;
1247cb6dcffSAndrew F. Davis 
1257cb6dcffSAndrew F. Davis 	ret = ina3221_read_value(ina, reg, &val);
1267cb6dcffSAndrew F. Davis 	if (ret)
1277cb6dcffSAndrew F. Davis 		return ret;
1287cb6dcffSAndrew F. Davis 
1297cb6dcffSAndrew F. Davis 	voltage_mv = val * 8;
1307cb6dcffSAndrew F. Davis 
1317cb6dcffSAndrew F. Davis 	return snprintf(buf, PAGE_SIZE, "%d\n", voltage_mv);
1327cb6dcffSAndrew F. Davis }
1337cb6dcffSAndrew F. Davis 
1347cb6dcffSAndrew F. Davis static ssize_t ina3221_show_shunt_voltage(struct device *dev,
1357cb6dcffSAndrew F. Davis 					  struct device_attribute *attr,
1367cb6dcffSAndrew F. Davis 					  char *buf)
1377cb6dcffSAndrew F. Davis {
1387cb6dcffSAndrew F. Davis 	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
1397cb6dcffSAndrew F. Davis 	struct ina3221_data *ina = dev_get_drvdata(dev);
1407cb6dcffSAndrew F. Davis 	unsigned int reg = sd_attr->index;
1417cb6dcffSAndrew F. Davis 	int val, voltage_uv, ret;
1427cb6dcffSAndrew F. Davis 
1437cb6dcffSAndrew F. Davis 	ret = ina3221_read_value(ina, reg, &val);
1447cb6dcffSAndrew F. Davis 	if (ret)
1457cb6dcffSAndrew F. Davis 		return ret;
1467cb6dcffSAndrew F. Davis 	voltage_uv = val * 40;
1477cb6dcffSAndrew F. Davis 
1487cb6dcffSAndrew F. Davis 	return snprintf(buf, PAGE_SIZE, "%d\n", voltage_uv);
1497cb6dcffSAndrew F. Davis }
1507cb6dcffSAndrew F. Davis 
1517cb6dcffSAndrew F. Davis static ssize_t ina3221_show_current(struct device *dev,
1527cb6dcffSAndrew F. Davis 				    struct device_attribute *attr, char *buf)
1537cb6dcffSAndrew F. Davis {
1547cb6dcffSAndrew F. Davis 	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
1557cb6dcffSAndrew F. Davis 	struct ina3221_data *ina = dev_get_drvdata(dev);
1567cb6dcffSAndrew F. Davis 	unsigned int reg = sd_attr->index;
1577cb6dcffSAndrew F. Davis 	unsigned int channel = register_channel[reg];
1589ad0df1aSGuenter Roeck 	int resistance_uo = ina->shunt_resistors[channel];
1597cb6dcffSAndrew F. Davis 	int val, current_ma, voltage_nv, ret;
1607cb6dcffSAndrew F. Davis 
1617cb6dcffSAndrew F. Davis 	ret = ina3221_read_value(ina, reg, &val);
1627cb6dcffSAndrew F. Davis 	if (ret)
1637cb6dcffSAndrew F. Davis 		return ret;
1647cb6dcffSAndrew F. Davis 	voltage_nv = val * 40000;
1657cb6dcffSAndrew F. Davis 
1667cb6dcffSAndrew F. Davis 	current_ma = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo);
1677cb6dcffSAndrew F. Davis 
1687cb6dcffSAndrew F. Davis 	return snprintf(buf, PAGE_SIZE, "%d\n", current_ma);
1697cb6dcffSAndrew F. Davis }
1707cb6dcffSAndrew F. Davis 
1717cb6dcffSAndrew F. Davis static ssize_t ina3221_set_current(struct device *dev,
1727cb6dcffSAndrew F. Davis 				   struct device_attribute *attr,
1737cb6dcffSAndrew F. Davis 				   const char *buf, size_t count)
1747cb6dcffSAndrew F. Davis {
1757cb6dcffSAndrew F. Davis 	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
1767cb6dcffSAndrew F. Davis 	struct ina3221_data *ina = dev_get_drvdata(dev);
1777cb6dcffSAndrew F. Davis 	unsigned int reg = sd_attr->index;
1787cb6dcffSAndrew F. Davis 	unsigned int channel = register_channel[reg];
1799ad0df1aSGuenter Roeck 	int resistance_uo = ina->shunt_resistors[channel];
1807cb6dcffSAndrew F. Davis 	int val, current_ma, voltage_uv, ret;
1817cb6dcffSAndrew F. Davis 
1827cb6dcffSAndrew F. Davis 	ret = kstrtoint(buf, 0, &current_ma);
1837cb6dcffSAndrew F. Davis 	if (ret)
1847cb6dcffSAndrew F. Davis 		return ret;
1857cb6dcffSAndrew F. Davis 
1867cb6dcffSAndrew F. Davis 	/* clamp current */
1877cb6dcffSAndrew F. Davis 	current_ma = clamp_val(current_ma,
1887cb6dcffSAndrew F. Davis 			       INT_MIN / resistance_uo,
1897cb6dcffSAndrew F. Davis 			       INT_MAX / resistance_uo);
1907cb6dcffSAndrew F. Davis 
1917cb6dcffSAndrew F. Davis 	voltage_uv = DIV_ROUND_CLOSEST(current_ma * resistance_uo, 1000);
1927cb6dcffSAndrew F. Davis 
1937cb6dcffSAndrew F. Davis 	/* clamp voltage */
1947cb6dcffSAndrew F. Davis 	voltage_uv = clamp_val(voltage_uv, -163800, 163800);
1957cb6dcffSAndrew F. Davis 
1967cb6dcffSAndrew F. Davis 	/* 1 / 40uV(scale) << 3(register shift) = 5 */
1977cb6dcffSAndrew F. Davis 	val = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8;
1987cb6dcffSAndrew F. Davis 
1997cb6dcffSAndrew F. Davis 	ret = regmap_write(ina->regmap, reg, val);
2007cb6dcffSAndrew F. Davis 	if (ret)
2017cb6dcffSAndrew F. Davis 		return ret;
2027cb6dcffSAndrew F. Davis 
2037cb6dcffSAndrew F. Davis 	return count;
2047cb6dcffSAndrew F. Davis }
2057cb6dcffSAndrew F. Davis 
2067cb6dcffSAndrew F. Davis static ssize_t ina3221_show_shunt(struct device *dev,
2077cb6dcffSAndrew F. Davis 				  struct device_attribute *attr, char *buf)
2087cb6dcffSAndrew F. Davis {
2097cb6dcffSAndrew F. Davis 	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
2107cb6dcffSAndrew F. Davis 	struct ina3221_data *ina = dev_get_drvdata(dev);
2117cb6dcffSAndrew F. Davis 	unsigned int channel = sd_attr->index;
2127cb6dcffSAndrew F. Davis 	unsigned int resistance_uo;
2137cb6dcffSAndrew F. Davis 
2147cb6dcffSAndrew F. Davis 	resistance_uo = ina->shunt_resistors[channel];
2157cb6dcffSAndrew F. Davis 
2167cb6dcffSAndrew F. Davis 	return snprintf(buf, PAGE_SIZE, "%d\n", resistance_uo);
2177cb6dcffSAndrew F. Davis }
2187cb6dcffSAndrew F. Davis 
2197cb6dcffSAndrew F. Davis static ssize_t ina3221_set_shunt(struct device *dev,
2207cb6dcffSAndrew F. Davis 				 struct device_attribute *attr,
2217cb6dcffSAndrew F. Davis 				 const char *buf, size_t count)
2227cb6dcffSAndrew F. Davis {
2237cb6dcffSAndrew F. Davis 	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
2247cb6dcffSAndrew F. Davis 	struct ina3221_data *ina = dev_get_drvdata(dev);
2257cb6dcffSAndrew F. Davis 	unsigned int channel = sd_attr->index;
2269ad0df1aSGuenter Roeck 	int val;
2277cb6dcffSAndrew F. Davis 	int ret;
2287cb6dcffSAndrew F. Davis 
2299ad0df1aSGuenter Roeck 	ret = kstrtoint(buf, 0, &val);
2307cb6dcffSAndrew F. Davis 	if (ret)
2317cb6dcffSAndrew F. Davis 		return ret;
2327cb6dcffSAndrew F. Davis 
2339ad0df1aSGuenter Roeck 	val = clamp_val(val, 1, INT_MAX);
2347cb6dcffSAndrew F. Davis 
2357cb6dcffSAndrew F. Davis 	ina->shunt_resistors[channel] = val;
2367cb6dcffSAndrew F. Davis 
2377cb6dcffSAndrew F. Davis 	return count;
2387cb6dcffSAndrew F. Davis }
2397cb6dcffSAndrew F. Davis 
2407cb6dcffSAndrew F. Davis static ssize_t ina3221_show_alert(struct device *dev,
2417cb6dcffSAndrew F. Davis 				  struct device_attribute *attr, char *buf)
2427cb6dcffSAndrew F. Davis {
2437cb6dcffSAndrew F. Davis 	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
2447cb6dcffSAndrew F. Davis 	struct ina3221_data *ina = dev_get_drvdata(dev);
2457cb6dcffSAndrew F. Davis 	unsigned int field = sd_attr->index;
2467cb6dcffSAndrew F. Davis 	unsigned int regval;
2477cb6dcffSAndrew F. Davis 	int ret;
2487cb6dcffSAndrew F. Davis 
2497cb6dcffSAndrew F. Davis 	ret = regmap_field_read(ina->fields[field], &regval);
2507cb6dcffSAndrew F. Davis 	if (ret)
2517cb6dcffSAndrew F. Davis 		return ret;
2527cb6dcffSAndrew F. Davis 
2537cb6dcffSAndrew F. Davis 	return snprintf(buf, PAGE_SIZE, "%d\n", regval);
2547cb6dcffSAndrew F. Davis }
2557cb6dcffSAndrew F. Davis 
2567cb6dcffSAndrew F. Davis /* bus voltage */
2577cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO,
2587cb6dcffSAndrew F. Davis 		ina3221_show_bus_voltage, NULL, INA3221_BUS1);
2597cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO,
2607cb6dcffSAndrew F. Davis 		ina3221_show_bus_voltage, NULL, INA3221_BUS2);
2617cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO,
2627cb6dcffSAndrew F. Davis 		ina3221_show_bus_voltage, NULL, INA3221_BUS3);
2637cb6dcffSAndrew F. Davis 
2647cb6dcffSAndrew F. Davis /* calculated current */
2657cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO,
2667cb6dcffSAndrew F. Davis 		ina3221_show_current, NULL, INA3221_SHUNT1);
2677cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO,
2687cb6dcffSAndrew F. Davis 		ina3221_show_current, NULL, INA3221_SHUNT2);
2697cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr3_input, S_IRUGO,
2707cb6dcffSAndrew F. Davis 		ina3221_show_current, NULL, INA3221_SHUNT3);
2717cb6dcffSAndrew F. Davis 
2727cb6dcffSAndrew F. Davis /* shunt resistance */
2737cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(shunt1_resistor, S_IRUGO | S_IWUSR,
2747cb6dcffSAndrew F. Davis 		ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL1);
2757cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(shunt2_resistor, S_IRUGO | S_IWUSR,
2767cb6dcffSAndrew F. Davis 		ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL2);
2777cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(shunt3_resistor, S_IRUGO | S_IWUSR,
2787cb6dcffSAndrew F. Davis 		ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL3);
2797cb6dcffSAndrew F. Davis 
2807cb6dcffSAndrew F. Davis /* critical current */
2817cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr1_crit, S_IRUGO | S_IWUSR,
2827cb6dcffSAndrew F. Davis 		ina3221_show_current, ina3221_set_current, INA3221_CRIT1);
2837cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr2_crit, S_IRUGO | S_IWUSR,
2847cb6dcffSAndrew F. Davis 		ina3221_show_current, ina3221_set_current, INA3221_CRIT2);
2857cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr3_crit, S_IRUGO | S_IWUSR,
2867cb6dcffSAndrew F. Davis 		ina3221_show_current, ina3221_set_current, INA3221_CRIT3);
2877cb6dcffSAndrew F. Davis 
2887cb6dcffSAndrew F. Davis /* critical current alert */
2897cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr1_crit_alarm, S_IRUGO,
2907cb6dcffSAndrew F. Davis 		ina3221_show_alert, NULL, F_CF1);
2917cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr2_crit_alarm, S_IRUGO,
2927cb6dcffSAndrew F. Davis 		ina3221_show_alert, NULL, F_CF2);
2937cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr3_crit_alarm, S_IRUGO,
2947cb6dcffSAndrew F. Davis 		ina3221_show_alert, NULL, F_CF3);
2957cb6dcffSAndrew F. Davis 
2967cb6dcffSAndrew F. Davis /* warning current */
2977cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr1_max, S_IRUGO | S_IWUSR,
2987cb6dcffSAndrew F. Davis 		ina3221_show_current, ina3221_set_current, INA3221_WARN1);
2997cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr2_max, S_IRUGO | S_IWUSR,
3007cb6dcffSAndrew F. Davis 		ina3221_show_current, ina3221_set_current, INA3221_WARN2);
3017cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr3_max, S_IRUGO | S_IWUSR,
3027cb6dcffSAndrew F. Davis 		ina3221_show_current, ina3221_set_current, INA3221_WARN3);
3037cb6dcffSAndrew F. Davis 
3047cb6dcffSAndrew F. Davis /* warning current alert */
3057cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO,
3067cb6dcffSAndrew F. Davis 		ina3221_show_alert, NULL, F_WF1);
3077cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr2_max_alarm, S_IRUGO,
3087cb6dcffSAndrew F. Davis 		ina3221_show_alert, NULL, F_WF2);
3097cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(curr3_max_alarm, S_IRUGO,
3107cb6dcffSAndrew F. Davis 		ina3221_show_alert, NULL, F_WF3);
3117cb6dcffSAndrew F. Davis 
3127cb6dcffSAndrew F. Davis /* shunt voltage */
3137cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO,
3147cb6dcffSAndrew F. Davis 		ina3221_show_shunt_voltage, NULL, INA3221_SHUNT1);
3157cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO,
3167cb6dcffSAndrew F. Davis 		ina3221_show_shunt_voltage, NULL, INA3221_SHUNT2);
3177cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO,
3187cb6dcffSAndrew F. Davis 		ina3221_show_shunt_voltage, NULL, INA3221_SHUNT3);
3197cb6dcffSAndrew F. Davis 
3207cb6dcffSAndrew F. Davis static struct attribute *ina3221_attrs[] = {
3217cb6dcffSAndrew F. Davis 	/* channel 1 */
3227cb6dcffSAndrew F. Davis 	&sensor_dev_attr_in1_input.dev_attr.attr,
3237cb6dcffSAndrew F. Davis 	&sensor_dev_attr_curr1_input.dev_attr.attr,
3247cb6dcffSAndrew F. Davis 	&sensor_dev_attr_shunt1_resistor.dev_attr.attr,
3257cb6dcffSAndrew F. Davis 	&sensor_dev_attr_curr1_crit.dev_attr.attr,
3267cb6dcffSAndrew F. Davis 	&sensor_dev_attr_curr1_crit_alarm.dev_attr.attr,
3277cb6dcffSAndrew F. Davis 	&sensor_dev_attr_curr1_max.dev_attr.attr,
3287cb6dcffSAndrew F. Davis 	&sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
3297cb6dcffSAndrew F. Davis 	&sensor_dev_attr_in4_input.dev_attr.attr,
3307cb6dcffSAndrew F. Davis 
3317cb6dcffSAndrew F. Davis 	/* channel 2 */
3327cb6dcffSAndrew F. Davis 	&sensor_dev_attr_in2_input.dev_attr.attr,
3337cb6dcffSAndrew F. Davis 	&sensor_dev_attr_curr2_input.dev_attr.attr,
3347cb6dcffSAndrew F. Davis 	&sensor_dev_attr_shunt2_resistor.dev_attr.attr,
3357cb6dcffSAndrew F. Davis 	&sensor_dev_attr_curr2_crit.dev_attr.attr,
3367cb6dcffSAndrew F. Davis 	&sensor_dev_attr_curr2_crit_alarm.dev_attr.attr,
3377cb6dcffSAndrew F. Davis 	&sensor_dev_attr_curr2_max.dev_attr.attr,
3387cb6dcffSAndrew F. Davis 	&sensor_dev_attr_curr2_max_alarm.dev_attr.attr,
3397cb6dcffSAndrew F. Davis 	&sensor_dev_attr_in5_input.dev_attr.attr,
3407cb6dcffSAndrew F. Davis 
3417cb6dcffSAndrew F. Davis 	/* channel 3 */
3427cb6dcffSAndrew F. Davis 	&sensor_dev_attr_in3_input.dev_attr.attr,
3437cb6dcffSAndrew F. Davis 	&sensor_dev_attr_curr3_input.dev_attr.attr,
3447cb6dcffSAndrew F. Davis 	&sensor_dev_attr_shunt3_resistor.dev_attr.attr,
3457cb6dcffSAndrew F. Davis 	&sensor_dev_attr_curr3_crit.dev_attr.attr,
3467cb6dcffSAndrew F. Davis 	&sensor_dev_attr_curr3_crit_alarm.dev_attr.attr,
3477cb6dcffSAndrew F. Davis 	&sensor_dev_attr_curr3_max.dev_attr.attr,
3487cb6dcffSAndrew F. Davis 	&sensor_dev_attr_curr3_max_alarm.dev_attr.attr,
3497cb6dcffSAndrew F. Davis 	&sensor_dev_attr_in6_input.dev_attr.attr,
3507cb6dcffSAndrew F. Davis 
3517cb6dcffSAndrew F. Davis 	NULL,
3527cb6dcffSAndrew F. Davis };
3537cb6dcffSAndrew F. Davis ATTRIBUTE_GROUPS(ina3221);
3547cb6dcffSAndrew F. Davis 
3557cb6dcffSAndrew F. Davis static const struct regmap_range ina3221_yes_ranges[] = {
356c20217b3SNicolin Chen 	regmap_reg_range(INA3221_CONFIG, INA3221_BUS3),
3577cb6dcffSAndrew F. Davis 	regmap_reg_range(INA3221_MASK_ENABLE, INA3221_MASK_ENABLE),
3587cb6dcffSAndrew F. Davis };
3597cb6dcffSAndrew F. Davis 
3607cb6dcffSAndrew F. Davis static const struct regmap_access_table ina3221_volatile_table = {
3617cb6dcffSAndrew F. Davis 	.yes_ranges = ina3221_yes_ranges,
3627cb6dcffSAndrew F. Davis 	.n_yes_ranges = ARRAY_SIZE(ina3221_yes_ranges),
3637cb6dcffSAndrew F. Davis };
3647cb6dcffSAndrew F. Davis 
3657cb6dcffSAndrew F. Davis static const struct regmap_config ina3221_regmap_config = {
3667cb6dcffSAndrew F. Davis 	.reg_bits = 8,
3677cb6dcffSAndrew F. Davis 	.val_bits = 16,
3687cb6dcffSAndrew F. Davis 
3697cb6dcffSAndrew F. Davis 	.cache_type = REGCACHE_RBTREE,
3707cb6dcffSAndrew F. Davis 	.volatile_table = &ina3221_volatile_table,
3717cb6dcffSAndrew F. Davis };
3727cb6dcffSAndrew F. Davis 
3737cb6dcffSAndrew F. Davis static int ina3221_probe(struct i2c_client *client,
3747cb6dcffSAndrew F. Davis 			 const struct i2c_device_id *id)
3757cb6dcffSAndrew F. Davis {
3767cb6dcffSAndrew F. Davis 	struct device *dev = &client->dev;
3777cb6dcffSAndrew F. Davis 	struct ina3221_data *ina;
3787cb6dcffSAndrew F. Davis 	struct device *hwmon_dev;
3797cb6dcffSAndrew F. Davis 	int i, ret;
3807cb6dcffSAndrew F. Davis 
3817cb6dcffSAndrew F. Davis 	ina = devm_kzalloc(dev, sizeof(*ina), GFP_KERNEL);
3827cb6dcffSAndrew F. Davis 	if (!ina)
3837cb6dcffSAndrew F. Davis 		return -ENOMEM;
3847cb6dcffSAndrew F. Davis 
3857cb6dcffSAndrew F. Davis 	ina->regmap = devm_regmap_init_i2c(client, &ina3221_regmap_config);
3867cb6dcffSAndrew F. Davis 	if (IS_ERR(ina->regmap)) {
3877cb6dcffSAndrew F. Davis 		dev_err(dev, "Unable to allocate register map\n");
3887cb6dcffSAndrew F. Davis 		return PTR_ERR(ina->regmap);
3897cb6dcffSAndrew F. Davis 	}
3907cb6dcffSAndrew F. Davis 
3917cb6dcffSAndrew F. Davis 	for (i = 0; i < F_MAX_FIELDS; i++) {
3927cb6dcffSAndrew F. Davis 		ina->fields[i] = devm_regmap_field_alloc(dev,
3937cb6dcffSAndrew F. Davis 							 ina->regmap,
3947cb6dcffSAndrew F. Davis 							 ina3221_reg_fields[i]);
3957cb6dcffSAndrew F. Davis 		if (IS_ERR(ina->fields[i])) {
3967cb6dcffSAndrew F. Davis 			dev_err(dev, "Unable to allocate regmap fields\n");
3977cb6dcffSAndrew F. Davis 			return PTR_ERR(ina->fields[i]);
3987cb6dcffSAndrew F. Davis 		}
3997cb6dcffSAndrew F. Davis 	}
4007cb6dcffSAndrew F. Davis 
4017cb6dcffSAndrew F. Davis 	for (i = 0; i < INA3221_NUM_CHANNELS; i++)
4027cb6dcffSAndrew F. Davis 		ina->shunt_resistors[i] = INA3221_RSHUNT_DEFAULT;
4037cb6dcffSAndrew F. Davis 
4047cb6dcffSAndrew F. Davis 	ret = regmap_field_write(ina->fields[F_RST], true);
4057cb6dcffSAndrew F. Davis 	if (ret) {
4067cb6dcffSAndrew F. Davis 		dev_err(dev, "Unable to reset device\n");
4077cb6dcffSAndrew F. Davis 		return ret;
4087cb6dcffSAndrew F. Davis 	}
4097cb6dcffSAndrew F. Davis 
4107cb6dcffSAndrew F. Davis 	hwmon_dev = devm_hwmon_device_register_with_groups(dev,
4117cb6dcffSAndrew F. Davis 							   client->name,
4127cb6dcffSAndrew F. Davis 							   ina, ina3221_groups);
4137cb6dcffSAndrew F. Davis 	if (IS_ERR(hwmon_dev)) {
4147cb6dcffSAndrew F. Davis 		dev_err(dev, "Unable to register hwmon device\n");
4157cb6dcffSAndrew F. Davis 		return PTR_ERR(hwmon_dev);
4167cb6dcffSAndrew F. Davis 	}
4177cb6dcffSAndrew F. Davis 
4187cb6dcffSAndrew F. Davis 	return 0;
4197cb6dcffSAndrew F. Davis }
4207cb6dcffSAndrew F. Davis 
4217cb6dcffSAndrew F. Davis static const struct of_device_id ina3221_of_match_table[] = {
4227cb6dcffSAndrew F. Davis 	{ .compatible = "ti,ina3221", },
4237cb6dcffSAndrew F. Davis 	{ /* sentinel */ }
4247cb6dcffSAndrew F. Davis };
4257cb6dcffSAndrew F. Davis MODULE_DEVICE_TABLE(of, ina3221_of_match_table);
4267cb6dcffSAndrew F. Davis 
4277cb6dcffSAndrew F. Davis static const struct i2c_device_id ina3221_ids[] = {
4287cb6dcffSAndrew F. Davis 	{ "ina3221", 0 },
4297cb6dcffSAndrew F. Davis 	{ /* sentinel */ }
4307cb6dcffSAndrew F. Davis };
4317cb6dcffSAndrew F. Davis MODULE_DEVICE_TABLE(i2c, ina3221_ids);
4327cb6dcffSAndrew F. Davis 
4337cb6dcffSAndrew F. Davis static struct i2c_driver ina3221_i2c_driver = {
4347cb6dcffSAndrew F. Davis 	.probe = ina3221_probe,
4357cb6dcffSAndrew F. Davis 	.driver = {
4367cb6dcffSAndrew F. Davis 		.name = INA3221_DRIVER_NAME,
4377cb6dcffSAndrew F. Davis 		.of_match_table = ina3221_of_match_table,
4387cb6dcffSAndrew F. Davis 	},
4397cb6dcffSAndrew F. Davis 	.id_table = ina3221_ids,
4407cb6dcffSAndrew F. Davis };
4417cb6dcffSAndrew F. Davis module_i2c_driver(ina3221_i2c_driver);
4427cb6dcffSAndrew F. Davis 
4437cb6dcffSAndrew F. Davis MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
4447cb6dcffSAndrew F. Davis MODULE_DESCRIPTION("Texas Instruments INA3221 HWMon Driver");
4457cb6dcffSAndrew F. Davis MODULE_LICENSE("GPL v2");
446