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