xref: /openbmc/linux/drivers/hwmon/ina3221.c (revision 023912db)
17cb6dcffSAndrew F. Davis /*
27cb6dcffSAndrew F. Davis  * INA3221 Triple Current/Voltage Monitor
37cb6dcffSAndrew F. Davis  *
47cb6dcffSAndrew F. Davis  * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/
57cb6dcffSAndrew F. Davis  *	Andrew F. Davis <afd@ti.com>
67cb6dcffSAndrew F. Davis  *
77cb6dcffSAndrew F. Davis  * This program is free software; you can redistribute it and/or modify
87cb6dcffSAndrew F. Davis  * it under the terms of the GNU General Public License version 2 as
97cb6dcffSAndrew F. Davis  * published by the Free Software Foundation.
107cb6dcffSAndrew F. Davis  *
117cb6dcffSAndrew F. Davis  * This program is distributed in the hope that it will be useful, but
127cb6dcffSAndrew F. Davis  * WITHOUT ANY WARRANTY; without even the implied warranty of
137cb6dcffSAndrew F. Davis  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
147cb6dcffSAndrew F. Davis  * General Public License for more details.
157cb6dcffSAndrew F. Davis  */
167cb6dcffSAndrew F. Davis 
177cb6dcffSAndrew F. Davis #include <linux/hwmon.h>
187cb6dcffSAndrew F. Davis #include <linux/hwmon-sysfs.h>
197cb6dcffSAndrew F. Davis #include <linux/i2c.h>
207cb6dcffSAndrew F. Davis #include <linux/module.h>
2187625b24SNicolin Chen #include <linux/mutex.h>
227cb6dcffSAndrew F. Davis #include <linux/of.h>
23323aeb0eSNicolin Chen #include <linux/pm_runtime.h>
247cb6dcffSAndrew F. Davis #include <linux/regmap.h>
255c090abfSNicolin Chen #include <linux/util_macros.h>
267cb6dcffSAndrew F. Davis 
277cb6dcffSAndrew F. Davis #define INA3221_DRIVER_NAME		"ina3221"
287cb6dcffSAndrew F. Davis 
297cb6dcffSAndrew F. Davis #define INA3221_CONFIG			0x00
307cb6dcffSAndrew F. Davis #define INA3221_SHUNT1			0x01
317cb6dcffSAndrew F. Davis #define INA3221_BUS1			0x02
327cb6dcffSAndrew F. Davis #define INA3221_SHUNT2			0x03
337cb6dcffSAndrew F. Davis #define INA3221_BUS2			0x04
347cb6dcffSAndrew F. Davis #define INA3221_SHUNT3			0x05
357cb6dcffSAndrew F. Davis #define INA3221_BUS3			0x06
367cb6dcffSAndrew F. Davis #define INA3221_CRIT1			0x07
377cb6dcffSAndrew F. Davis #define INA3221_WARN1			0x08
387cb6dcffSAndrew F. Davis #define INA3221_CRIT2			0x09
397cb6dcffSAndrew F. Davis #define INA3221_WARN2			0x0a
407cb6dcffSAndrew F. Davis #define INA3221_CRIT3			0x0b
417cb6dcffSAndrew F. Davis #define INA3221_WARN3			0x0c
427cb6dcffSAndrew F. Davis #define INA3221_MASK_ENABLE		0x0f
437cb6dcffSAndrew F. Davis 
4459d608e1SNicolin Chen #define INA3221_CONFIG_MODE_MASK	GENMASK(2, 0)
4559d608e1SNicolin Chen #define INA3221_CONFIG_MODE_POWERDOWN	0
46791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_SHUNT	BIT(0)
47791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_BUS		BIT(1)
48791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_CONTINUOUS	BIT(2)
494c0415a3SNicolin Chen #define INA3221_CONFIG_VSH_CT_SHIFT	3
504c0415a3SNicolin Chen #define INA3221_CONFIG_VSH_CT_MASK	GENMASK(5, 3)
514c0415a3SNicolin Chen #define INA3221_CONFIG_VSH_CT(x)	(((x) & GENMASK(5, 3)) >> 3)
524c0415a3SNicolin Chen #define INA3221_CONFIG_VBUS_CT_SHIFT	6
534c0415a3SNicolin Chen #define INA3221_CONFIG_VBUS_CT_MASK	GENMASK(8, 6)
544c0415a3SNicolin Chen #define INA3221_CONFIG_VBUS_CT(x)	(((x) & GENMASK(8, 6)) >> 6)
555c090abfSNicolin Chen #define INA3221_CONFIG_AVG_SHIFT	9
565c090abfSNicolin Chen #define INA3221_CONFIG_AVG_MASK		GENMASK(11, 9)
575c090abfSNicolin Chen #define INA3221_CONFIG_AVG(x)		(((x) & GENMASK(11, 9)) >> 9)
584c0415a3SNicolin Chen #define INA3221_CONFIG_CHs_EN_MASK	GENMASK(14, 12)
59a9e9dd9cSNicolin Chen #define INA3221_CONFIG_CHx_EN(x)	BIT(14 - (x))
607cb6dcffSAndrew F. Davis 
61323aeb0eSNicolin Chen #define INA3221_CONFIG_DEFAULT		0x7127
627cb6dcffSAndrew F. Davis #define INA3221_RSHUNT_DEFAULT		10000
637cb6dcffSAndrew F. Davis 
647cb6dcffSAndrew F. Davis enum ina3221_fields {
657cb6dcffSAndrew F. Davis 	/* Configuration */
667cb6dcffSAndrew F. Davis 	F_RST,
677cb6dcffSAndrew F. Davis 
684c0415a3SNicolin Chen 	/* Status Flags */
694c0415a3SNicolin Chen 	F_CVRF,
704c0415a3SNicolin Chen 
717cb6dcffSAndrew F. Davis 	/* Alert Flags */
727cb6dcffSAndrew F. Davis 	F_WF3, F_WF2, F_WF1,
737cb6dcffSAndrew F. Davis 	F_CF3, F_CF2, F_CF1,
747cb6dcffSAndrew F. Davis 
757cb6dcffSAndrew F. Davis 	/* sentinel */
767cb6dcffSAndrew F. Davis 	F_MAX_FIELDS
777cb6dcffSAndrew F. Davis };
787cb6dcffSAndrew F. Davis 
797cb6dcffSAndrew F. Davis static const struct reg_field ina3221_reg_fields[] = {
807cb6dcffSAndrew F. Davis 	[F_RST] = REG_FIELD(INA3221_CONFIG, 15, 15),
817cb6dcffSAndrew F. Davis 
824c0415a3SNicolin Chen 	[F_CVRF] = REG_FIELD(INA3221_MASK_ENABLE, 0, 0),
837cb6dcffSAndrew F. Davis 	[F_WF3] = REG_FIELD(INA3221_MASK_ENABLE, 3, 3),
847cb6dcffSAndrew F. Davis 	[F_WF2] = REG_FIELD(INA3221_MASK_ENABLE, 4, 4),
857cb6dcffSAndrew F. Davis 	[F_WF1] = REG_FIELD(INA3221_MASK_ENABLE, 5, 5),
867cb6dcffSAndrew F. Davis 	[F_CF3] = REG_FIELD(INA3221_MASK_ENABLE, 7, 7),
877cb6dcffSAndrew F. Davis 	[F_CF2] = REG_FIELD(INA3221_MASK_ENABLE, 8, 8),
887cb6dcffSAndrew F. Davis 	[F_CF1] = REG_FIELD(INA3221_MASK_ENABLE, 9, 9),
897cb6dcffSAndrew F. Davis };
907cb6dcffSAndrew F. Davis 
917cb6dcffSAndrew F. Davis enum ina3221_channels {
927cb6dcffSAndrew F. Davis 	INA3221_CHANNEL1,
937cb6dcffSAndrew F. Davis 	INA3221_CHANNEL2,
947cb6dcffSAndrew F. Davis 	INA3221_CHANNEL3,
957cb6dcffSAndrew F. Davis 	INA3221_NUM_CHANNELS
967cb6dcffSAndrew F. Davis };
977cb6dcffSAndrew F. Davis 
987cb6dcffSAndrew F. Davis /**
99a9e9dd9cSNicolin Chen  * struct ina3221_input - channel input source specific information
100a9e9dd9cSNicolin Chen  * @label: label of channel input source
101a9e9dd9cSNicolin Chen  * @shunt_resistor: shunt resistor value of channel input source
102a9e9dd9cSNicolin Chen  * @disconnected: connection status of channel input source
103a9e9dd9cSNicolin Chen  */
104a9e9dd9cSNicolin Chen struct ina3221_input {
105a9e9dd9cSNicolin Chen 	const char *label;
106a9e9dd9cSNicolin Chen 	int shunt_resistor;
107a9e9dd9cSNicolin Chen 	bool disconnected;
108a9e9dd9cSNicolin Chen };
109a9e9dd9cSNicolin Chen 
110a9e9dd9cSNicolin Chen /**
1117cb6dcffSAndrew F. Davis  * struct ina3221_data - device specific information
112323aeb0eSNicolin Chen  * @pm_dev: Device pointer for pm runtime
1137cb6dcffSAndrew F. Davis  * @regmap: Register map of the device
1147cb6dcffSAndrew F. Davis  * @fields: Register fields of the device
115a9e9dd9cSNicolin Chen  * @inputs: Array of channel input source specific structures
11687625b24SNicolin Chen  * @lock: mutex lock to serialize sysfs attribute accesses
11759d608e1SNicolin Chen  * @reg_config: Register value of INA3221_CONFIG
11843dece16SNicolin Chen  * @single_shot: running in single-shot operating mode
1197cb6dcffSAndrew F. Davis  */
1207cb6dcffSAndrew F. Davis struct ina3221_data {
121323aeb0eSNicolin Chen 	struct device *pm_dev;
1227cb6dcffSAndrew F. Davis 	struct regmap *regmap;
1237cb6dcffSAndrew F. Davis 	struct regmap_field *fields[F_MAX_FIELDS];
124a9e9dd9cSNicolin Chen 	struct ina3221_input inputs[INA3221_NUM_CHANNELS];
12587625b24SNicolin Chen 	struct mutex lock;
12659d608e1SNicolin Chen 	u32 reg_config;
12743dece16SNicolin Chen 
12843dece16SNicolin Chen 	bool single_shot;
1297cb6dcffSAndrew F. Davis };
1307cb6dcffSAndrew F. Davis 
131a9e9dd9cSNicolin Chen static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel)
132a9e9dd9cSNicolin Chen {
133323aeb0eSNicolin Chen 	return pm_runtime_active(ina->pm_dev) &&
134323aeb0eSNicolin Chen 	       (ina->reg_config & INA3221_CONFIG_CHx_EN(channel));
135a9e9dd9cSNicolin Chen }
136a9e9dd9cSNicolin Chen 
1374c0415a3SNicolin Chen /* Lookup table for Bus and Shunt conversion times in usec */
1384c0415a3SNicolin Chen static const u16 ina3221_conv_time[] = {
1394c0415a3SNicolin Chen 	140, 204, 332, 588, 1100, 2116, 4156, 8244,
1404c0415a3SNicolin Chen };
1414c0415a3SNicolin Chen 
1425c090abfSNicolin Chen /* Lookup table for number of samples using in averaging mode */
1435c090abfSNicolin Chen static const int ina3221_avg_samples[] = {
1445c090abfSNicolin Chen 	1, 4, 16, 64, 128, 256, 512, 1024,
1455c090abfSNicolin Chen };
1465c090abfSNicolin Chen 
147023912dbSNicolin Chen /* Converting update_interval in msec to conversion time in usec */
148023912dbSNicolin Chen static inline u32 ina3221_interval_ms_to_conv_time(u16 config, int interval)
1494c0415a3SNicolin Chen {
150023912dbSNicolin Chen 	u32 channels = hweight16(config & INA3221_CONFIG_CHs_EN_MASK);
151023912dbSNicolin Chen 	u32 samples_idx = INA3221_CONFIG_AVG(config);
152023912dbSNicolin Chen 	u32 samples = ina3221_avg_samples[samples_idx];
153023912dbSNicolin Chen 
154023912dbSNicolin Chen 	/* Bisect the result to Bus and Shunt conversion times */
155023912dbSNicolin Chen 	return DIV_ROUND_CLOSEST(interval * 1000 / 2, channels * samples);
156023912dbSNicolin Chen }
157023912dbSNicolin Chen 
158023912dbSNicolin Chen /* Converting CONFIG register value to update_interval in usec */
159023912dbSNicolin Chen static inline u32 ina3221_reg_to_interval_us(u16 config)
160023912dbSNicolin Chen {
161023912dbSNicolin Chen 	u32 channels = hweight16(config & INA3221_CONFIG_CHs_EN_MASK);
162023912dbSNicolin Chen 	u32 vbus_ct_idx = INA3221_CONFIG_VBUS_CT(config);
163023912dbSNicolin Chen 	u32 vsh_ct_idx = INA3221_CONFIG_VSH_CT(config);
164023912dbSNicolin Chen 	u32 samples_idx = INA3221_CONFIG_AVG(config);
1655c090abfSNicolin Chen 	u32 samples = ina3221_avg_samples[samples_idx];
1664c0415a3SNicolin Chen 	u32 vbus_ct = ina3221_conv_time[vbus_ct_idx];
1674c0415a3SNicolin Chen 	u32 vsh_ct = ina3221_conv_time[vsh_ct_idx];
1684c0415a3SNicolin Chen 
1694c0415a3SNicolin Chen 	/* Calculate total conversion time */
170023912dbSNicolin Chen 	return channels * (vbus_ct + vsh_ct) * samples;
171023912dbSNicolin Chen }
172023912dbSNicolin Chen 
173023912dbSNicolin Chen static inline int ina3221_wait_for_data(struct ina3221_data *ina)
174023912dbSNicolin Chen {
175023912dbSNicolin Chen 	u32 wait, cvrf;
176023912dbSNicolin Chen 
177023912dbSNicolin Chen 	wait = ina3221_reg_to_interval_us(ina->reg_config);
1784c0415a3SNicolin Chen 
1794c0415a3SNicolin Chen 	/* Polling the CVRF bit to make sure read data is ready */
1804c0415a3SNicolin Chen 	return regmap_field_read_poll_timeout(ina->fields[F_CVRF],
1814c0415a3SNicolin Chen 					      cvrf, cvrf, wait, 100000);
1824c0415a3SNicolin Chen }
1834c0415a3SNicolin Chen 
1847cb6dcffSAndrew F. Davis static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg,
1857cb6dcffSAndrew F. Davis 			      int *val)
1867cb6dcffSAndrew F. Davis {
1877cb6dcffSAndrew F. Davis 	unsigned int regval;
1887cb6dcffSAndrew F. Davis 	int ret;
1897cb6dcffSAndrew F. Davis 
1907cb6dcffSAndrew F. Davis 	ret = regmap_read(ina->regmap, reg, &regval);
1917cb6dcffSAndrew F. Davis 	if (ret)
1927cb6dcffSAndrew F. Davis 		return ret;
1937cb6dcffSAndrew F. Davis 
1947cb6dcffSAndrew F. Davis 	*val = sign_extend32(regval >> 3, 12);
1957cb6dcffSAndrew F. Davis 
1967cb6dcffSAndrew F. Davis 	return 0;
1977cb6dcffSAndrew F. Davis }
1987cb6dcffSAndrew F. Davis 
199d4b0166dSNicolin Chen static const u8 ina3221_in_reg[] = {
200d4b0166dSNicolin Chen 	INA3221_BUS1,
201d4b0166dSNicolin Chen 	INA3221_BUS2,
202d4b0166dSNicolin Chen 	INA3221_BUS3,
203d4b0166dSNicolin Chen 	INA3221_SHUNT1,
204d4b0166dSNicolin Chen 	INA3221_SHUNT2,
205d4b0166dSNicolin Chen 	INA3221_SHUNT3,
206d4b0166dSNicolin Chen };
2077cb6dcffSAndrew F. Davis 
2085c090abfSNicolin Chen static int ina3221_read_chip(struct device *dev, u32 attr, long *val)
2095c090abfSNicolin Chen {
2105c090abfSNicolin Chen 	struct ina3221_data *ina = dev_get_drvdata(dev);
2115c090abfSNicolin Chen 	int regval;
2125c090abfSNicolin Chen 
2135c090abfSNicolin Chen 	switch (attr) {
2145c090abfSNicolin Chen 	case hwmon_chip_samples:
2155c090abfSNicolin Chen 		regval = INA3221_CONFIG_AVG(ina->reg_config);
2165c090abfSNicolin Chen 		*val = ina3221_avg_samples[regval];
2175c090abfSNicolin Chen 		return 0;
218023912dbSNicolin Chen 	case hwmon_chip_update_interval:
219023912dbSNicolin Chen 		/* Return in msec */
220023912dbSNicolin Chen 		*val = ina3221_reg_to_interval_us(ina->reg_config);
221023912dbSNicolin Chen 		*val = DIV_ROUND_CLOSEST(*val, 1000);
222023912dbSNicolin Chen 		return 0;
2235c090abfSNicolin Chen 	default:
2245c090abfSNicolin Chen 		return -EOPNOTSUPP;
2255c090abfSNicolin Chen 	}
2265c090abfSNicolin Chen }
2275c090abfSNicolin Chen 
228d4b0166dSNicolin Chen static int ina3221_read_in(struct device *dev, u32 attr, int channel, long *val)
229d4b0166dSNicolin Chen {
230d4b0166dSNicolin Chen 	const bool is_shunt = channel > INA3221_CHANNEL3;
231d4b0166dSNicolin Chen 	struct ina3221_data *ina = dev_get_drvdata(dev);
232d4b0166dSNicolin Chen 	u8 reg = ina3221_in_reg[channel];
233d4b0166dSNicolin Chen 	int regval, ret;
234d4b0166dSNicolin Chen 
235d4b0166dSNicolin Chen 	/* Translate shunt channel index to sensor channel index */
236d4b0166dSNicolin Chen 	channel %= INA3221_NUM_CHANNELS;
237d4b0166dSNicolin Chen 
238d4b0166dSNicolin Chen 	switch (attr) {
239d4b0166dSNicolin Chen 	case hwmon_in_input:
240d4b0166dSNicolin Chen 		if (!ina3221_is_enabled(ina, channel))
241a9e9dd9cSNicolin Chen 			return -ENODATA;
242a9e9dd9cSNicolin Chen 
24343dece16SNicolin Chen 		/* Write CONFIG register to trigger a single-shot measurement */
24443dece16SNicolin Chen 		if (ina->single_shot)
24543dece16SNicolin Chen 			regmap_write(ina->regmap, INA3221_CONFIG,
24643dece16SNicolin Chen 				     ina->reg_config);
24743dece16SNicolin Chen 
2484c0415a3SNicolin Chen 		ret = ina3221_wait_for_data(ina);
2494c0415a3SNicolin Chen 		if (ret)
2504c0415a3SNicolin Chen 			return ret;
2514c0415a3SNicolin Chen 
252d4b0166dSNicolin Chen 		ret = ina3221_read_value(ina, reg, &regval);
2537cb6dcffSAndrew F. Davis 		if (ret)
2547cb6dcffSAndrew F. Davis 			return ret;
2557cb6dcffSAndrew F. Davis 
256d4b0166dSNicolin Chen 		/*
257d4b0166dSNicolin Chen 		 * Scale of shunt voltage (uV): LSB is 40uV
258d4b0166dSNicolin Chen 		 * Scale of bus voltage (mV): LSB is 8mV
259d4b0166dSNicolin Chen 		 */
260d4b0166dSNicolin Chen 		*val = regval * (is_shunt ? 40 : 8);
261d4b0166dSNicolin Chen 		return 0;
262d4b0166dSNicolin Chen 	case hwmon_in_enable:
263d4b0166dSNicolin Chen 		*val = ina3221_is_enabled(ina, channel);
264d4b0166dSNicolin Chen 		return 0;
265d4b0166dSNicolin Chen 	default:
266d4b0166dSNicolin Chen 		return -EOPNOTSUPP;
267d4b0166dSNicolin Chen 	}
2687cb6dcffSAndrew F. Davis }
2697cb6dcffSAndrew F. Davis 
270d4b0166dSNicolin Chen static const u8 ina3221_curr_reg[][INA3221_NUM_CHANNELS] = {
271d4b0166dSNicolin Chen 	[hwmon_curr_input] = { INA3221_SHUNT1, INA3221_SHUNT2, INA3221_SHUNT3 },
272d4b0166dSNicolin Chen 	[hwmon_curr_max] = { INA3221_WARN1, INA3221_WARN2, INA3221_WARN3 },
273d4b0166dSNicolin Chen 	[hwmon_curr_crit] = { INA3221_CRIT1, INA3221_CRIT2, INA3221_CRIT3 },
274d4b0166dSNicolin Chen 	[hwmon_curr_max_alarm] = { F_WF1, F_WF2, F_WF3 },
275d4b0166dSNicolin Chen 	[hwmon_curr_crit_alarm] = { F_CF1, F_CF2, F_CF3 },
276d4b0166dSNicolin Chen };
277d4b0166dSNicolin Chen 
278d4b0166dSNicolin Chen static int ina3221_read_curr(struct device *dev, u32 attr,
279d4b0166dSNicolin Chen 			     int channel, long *val)
2807cb6dcffSAndrew F. Davis {
2817cb6dcffSAndrew F. Davis 	struct ina3221_data *ina = dev_get_drvdata(dev);
282a9e9dd9cSNicolin Chen 	struct ina3221_input *input = &ina->inputs[channel];
283a9e9dd9cSNicolin Chen 	int resistance_uo = input->shunt_resistor;
284d4b0166dSNicolin Chen 	u8 reg = ina3221_curr_reg[attr][channel];
285d4b0166dSNicolin Chen 	int regval, voltage_nv, ret;
2867cb6dcffSAndrew F. Davis 
287d4b0166dSNicolin Chen 	switch (attr) {
288d4b0166dSNicolin Chen 	case hwmon_curr_input:
289d4b0166dSNicolin Chen 		if (!ina3221_is_enabled(ina, channel))
290a9e9dd9cSNicolin Chen 			return -ENODATA;
2914c0415a3SNicolin Chen 
29243dece16SNicolin Chen 		/* Write CONFIG register to trigger a single-shot measurement */
29343dece16SNicolin Chen 		if (ina->single_shot)
29443dece16SNicolin Chen 			regmap_write(ina->regmap, INA3221_CONFIG,
29543dece16SNicolin Chen 				     ina->reg_config);
29643dece16SNicolin Chen 
2974c0415a3SNicolin Chen 		ret = ina3221_wait_for_data(ina);
2984c0415a3SNicolin Chen 		if (ret)
2994c0415a3SNicolin Chen 			return ret;
3004c0415a3SNicolin Chen 
301d4b0166dSNicolin Chen 		/* fall through */
302d4b0166dSNicolin Chen 	case hwmon_curr_crit:
303d4b0166dSNicolin Chen 	case hwmon_curr_max:
304d4b0166dSNicolin Chen 		ret = ina3221_read_value(ina, reg, &regval);
3057cb6dcffSAndrew F. Davis 		if (ret)
3067cb6dcffSAndrew F. Davis 			return ret;
3077cb6dcffSAndrew F. Davis 
308d4b0166dSNicolin Chen 		/* Scale of shunt voltage: LSB is 40uV (40000nV) */
309d4b0166dSNicolin Chen 		voltage_nv = regval * 40000;
310d4b0166dSNicolin Chen 		/* Return current in mA */
311d4b0166dSNicolin Chen 		*val = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo);
312d4b0166dSNicolin Chen 		return 0;
313d4b0166dSNicolin Chen 	case hwmon_curr_crit_alarm:
314d4b0166dSNicolin Chen 	case hwmon_curr_max_alarm:
315efb0489eSNicolin Chen 		/* No actual register read if channel is disabled */
316efb0489eSNicolin Chen 		if (!ina3221_is_enabled(ina, channel)) {
317efb0489eSNicolin Chen 			/* Return 0 for alert flags */
318efb0489eSNicolin Chen 			*val = 0;
319efb0489eSNicolin Chen 			return 0;
320efb0489eSNicolin Chen 		}
321d4b0166dSNicolin Chen 		ret = regmap_field_read(ina->fields[reg], &regval);
322d4b0166dSNicolin Chen 		if (ret)
323d4b0166dSNicolin Chen 			return ret;
324d4b0166dSNicolin Chen 		*val = regval;
325d4b0166dSNicolin Chen 		return 0;
326d4b0166dSNicolin Chen 	default:
327d4b0166dSNicolin Chen 		return -EOPNOTSUPP;
328d4b0166dSNicolin Chen 	}
3297cb6dcffSAndrew F. Davis }
3307cb6dcffSAndrew F. Davis 
3315c090abfSNicolin Chen static int ina3221_write_chip(struct device *dev, u32 attr, long val)
3325c090abfSNicolin Chen {
3335c090abfSNicolin Chen 	struct ina3221_data *ina = dev_get_drvdata(dev);
3345c090abfSNicolin Chen 	int ret, idx;
335521c0b61SNicolin Chen 	u32 tmp;
3365c090abfSNicolin Chen 
3375c090abfSNicolin Chen 	switch (attr) {
3385c090abfSNicolin Chen 	case hwmon_chip_samples:
3395c090abfSNicolin Chen 		idx = find_closest(val, ina3221_avg_samples,
3405c090abfSNicolin Chen 				   ARRAY_SIZE(ina3221_avg_samples));
3415c090abfSNicolin Chen 
342521c0b61SNicolin Chen 		tmp = (ina->reg_config & ~INA3221_CONFIG_AVG_MASK) |
343521c0b61SNicolin Chen 		      (idx << INA3221_CONFIG_AVG_SHIFT);
344521c0b61SNicolin Chen 		ret = regmap_write(ina->regmap, INA3221_CONFIG, tmp);
3455c090abfSNicolin Chen 		if (ret)
3465c090abfSNicolin Chen 			return ret;
3475c090abfSNicolin Chen 
3485c090abfSNicolin Chen 		/* Update reg_config accordingly */
349521c0b61SNicolin Chen 		ina->reg_config = tmp;
350521c0b61SNicolin Chen 		return 0;
351023912dbSNicolin Chen 	case hwmon_chip_update_interval:
352023912dbSNicolin Chen 		tmp = ina3221_interval_ms_to_conv_time(ina->reg_config, val);
353023912dbSNicolin Chen 		idx = find_closest(tmp, ina3221_conv_time,
354023912dbSNicolin Chen 				   ARRAY_SIZE(ina3221_conv_time));
355023912dbSNicolin Chen 
356023912dbSNicolin Chen 		/* Update Bus and Shunt voltage conversion times */
357023912dbSNicolin Chen 		tmp = INA3221_CONFIG_VBUS_CT_MASK | INA3221_CONFIG_VSH_CT_MASK;
358023912dbSNicolin Chen 		tmp = (ina->reg_config & ~tmp) |
359023912dbSNicolin Chen 		      (idx << INA3221_CONFIG_VBUS_CT_SHIFT) |
360023912dbSNicolin Chen 		      (idx << INA3221_CONFIG_VSH_CT_SHIFT);
361023912dbSNicolin Chen 		ret = regmap_write(ina->regmap, INA3221_CONFIG, tmp);
362023912dbSNicolin Chen 		if (ret)
363023912dbSNicolin Chen 			return ret;
364023912dbSNicolin Chen 
365023912dbSNicolin Chen 		/* Update reg_config accordingly */
366023912dbSNicolin Chen 		ina->reg_config = tmp;
367023912dbSNicolin Chen 		return 0;
3685c090abfSNicolin Chen 	default:
3695c090abfSNicolin Chen 		return -EOPNOTSUPP;
3705c090abfSNicolin Chen 	}
3715c090abfSNicolin Chen }
3725c090abfSNicolin Chen 
373d4b0166dSNicolin Chen static int ina3221_write_curr(struct device *dev, u32 attr,
374d4b0166dSNicolin Chen 			      int channel, long val)
3757cb6dcffSAndrew F. Davis {
3767cb6dcffSAndrew F. Davis 	struct ina3221_data *ina = dev_get_drvdata(dev);
377a9e9dd9cSNicolin Chen 	struct ina3221_input *input = &ina->inputs[channel];
378a9e9dd9cSNicolin Chen 	int resistance_uo = input->shunt_resistor;
379d4b0166dSNicolin Chen 	u8 reg = ina3221_curr_reg[attr][channel];
380d4b0166dSNicolin Chen 	int regval, current_ma, voltage_uv;
3817cb6dcffSAndrew F. Davis 
3827cb6dcffSAndrew F. Davis 	/* clamp current */
383d4b0166dSNicolin Chen 	current_ma = clamp_val(val,
3847cb6dcffSAndrew F. Davis 			       INT_MIN / resistance_uo,
3857cb6dcffSAndrew F. Davis 			       INT_MAX / resistance_uo);
3867cb6dcffSAndrew F. Davis 
3877cb6dcffSAndrew F. Davis 	voltage_uv = DIV_ROUND_CLOSEST(current_ma * resistance_uo, 1000);
3887cb6dcffSAndrew F. Davis 
3897cb6dcffSAndrew F. Davis 	/* clamp voltage */
3907cb6dcffSAndrew F. Davis 	voltage_uv = clamp_val(voltage_uv, -163800, 163800);
3917cb6dcffSAndrew F. Davis 
3927cb6dcffSAndrew F. Davis 	/* 1 / 40uV(scale) << 3(register shift) = 5 */
393d4b0166dSNicolin Chen 	regval = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8;
3947cb6dcffSAndrew F. Davis 
395d4b0166dSNicolin Chen 	return regmap_write(ina->regmap, reg, regval);
396d4b0166dSNicolin Chen }
397d4b0166dSNicolin Chen 
398d4b0166dSNicolin Chen static int ina3221_write_enable(struct device *dev, int channel, bool enable)
399d4b0166dSNicolin Chen {
400d4b0166dSNicolin Chen 	struct ina3221_data *ina = dev_get_drvdata(dev);
401d4b0166dSNicolin Chen 	u16 config, mask = INA3221_CONFIG_CHx_EN(channel);
402323aeb0eSNicolin Chen 	u16 config_old = ina->reg_config & mask;
403521c0b61SNicolin Chen 	u32 tmp;
404d4b0166dSNicolin Chen 	int ret;
405d4b0166dSNicolin Chen 
406d4b0166dSNicolin Chen 	config = enable ? mask : 0;
407d4b0166dSNicolin Chen 
408323aeb0eSNicolin Chen 	/* Bypass if enable status is not being changed */
409323aeb0eSNicolin Chen 	if (config_old == config)
410323aeb0eSNicolin Chen 		return 0;
411323aeb0eSNicolin Chen 
412323aeb0eSNicolin Chen 	/* For enabling routine, increase refcount and resume() at first */
413323aeb0eSNicolin Chen 	if (enable) {
414323aeb0eSNicolin Chen 		ret = pm_runtime_get_sync(ina->pm_dev);
415323aeb0eSNicolin Chen 		if (ret < 0) {
416323aeb0eSNicolin Chen 			dev_err(dev, "Failed to get PM runtime\n");
417323aeb0eSNicolin Chen 			return ret;
418323aeb0eSNicolin Chen 		}
419323aeb0eSNicolin Chen 	}
420323aeb0eSNicolin Chen 
421d4b0166dSNicolin Chen 	/* Enable or disable the channel */
422521c0b61SNicolin Chen 	tmp = (ina->reg_config & ~mask) | (config & mask);
423521c0b61SNicolin Chen 	ret = regmap_write(ina->regmap, INA3221_CONFIG, tmp);
4247cb6dcffSAndrew F. Davis 	if (ret)
425323aeb0eSNicolin Chen 		goto fail;
4267cb6dcffSAndrew F. Davis 
427d4b0166dSNicolin Chen 	/* Cache the latest config register value */
428521c0b61SNicolin Chen 	ina->reg_config = tmp;
429323aeb0eSNicolin Chen 
430323aeb0eSNicolin Chen 	/* For disabling routine, decrease refcount or suspend() at last */
431323aeb0eSNicolin Chen 	if (!enable)
432323aeb0eSNicolin Chen 		pm_runtime_put_sync(ina->pm_dev);
433d4b0166dSNicolin Chen 
434d4b0166dSNicolin Chen 	return 0;
435323aeb0eSNicolin Chen 
436323aeb0eSNicolin Chen fail:
437323aeb0eSNicolin Chen 	if (enable) {
438323aeb0eSNicolin Chen 		dev_err(dev, "Failed to enable channel %d: error %d\n",
439323aeb0eSNicolin Chen 			channel, ret);
440323aeb0eSNicolin Chen 		pm_runtime_put_sync(ina->pm_dev);
441323aeb0eSNicolin Chen 	}
442323aeb0eSNicolin Chen 
443323aeb0eSNicolin Chen 	return ret;
4447cb6dcffSAndrew F. Davis }
4457cb6dcffSAndrew F. Davis 
446d4b0166dSNicolin Chen static int ina3221_read(struct device *dev, enum hwmon_sensor_types type,
447d4b0166dSNicolin Chen 			u32 attr, int channel, long *val)
448d4b0166dSNicolin Chen {
44987625b24SNicolin Chen 	struct ina3221_data *ina = dev_get_drvdata(dev);
45087625b24SNicolin Chen 	int ret;
45187625b24SNicolin Chen 
45287625b24SNicolin Chen 	mutex_lock(&ina->lock);
45387625b24SNicolin Chen 
454d4b0166dSNicolin Chen 	switch (type) {
4555c090abfSNicolin Chen 	case hwmon_chip:
4565c090abfSNicolin Chen 		ret = ina3221_read_chip(dev, attr, val);
4575c090abfSNicolin Chen 		break;
458d4b0166dSNicolin Chen 	case hwmon_in:
459d4b0166dSNicolin Chen 		/* 0-align channel ID */
46087625b24SNicolin Chen 		ret = ina3221_read_in(dev, attr, channel - 1, val);
46187625b24SNicolin Chen 		break;
462d4b0166dSNicolin Chen 	case hwmon_curr:
46387625b24SNicolin Chen 		ret = ina3221_read_curr(dev, attr, channel, val);
46487625b24SNicolin Chen 		break;
465d4b0166dSNicolin Chen 	default:
46687625b24SNicolin Chen 		ret = -EOPNOTSUPP;
46787625b24SNicolin Chen 		break;
468d4b0166dSNicolin Chen 	}
46987625b24SNicolin Chen 
47087625b24SNicolin Chen 	mutex_unlock(&ina->lock);
47187625b24SNicolin Chen 
47287625b24SNicolin Chen 	return ret;
473d4b0166dSNicolin Chen }
474d4b0166dSNicolin Chen 
475d4b0166dSNicolin Chen static int ina3221_write(struct device *dev, enum hwmon_sensor_types type,
476d4b0166dSNicolin Chen 			 u32 attr, int channel, long val)
477d4b0166dSNicolin Chen {
47887625b24SNicolin Chen 	struct ina3221_data *ina = dev_get_drvdata(dev);
47987625b24SNicolin Chen 	int ret;
48087625b24SNicolin Chen 
48187625b24SNicolin Chen 	mutex_lock(&ina->lock);
48287625b24SNicolin Chen 
483d4b0166dSNicolin Chen 	switch (type) {
4845c090abfSNicolin Chen 	case hwmon_chip:
4855c090abfSNicolin Chen 		ret = ina3221_write_chip(dev, attr, val);
4865c090abfSNicolin Chen 		break;
487d4b0166dSNicolin Chen 	case hwmon_in:
488d4b0166dSNicolin Chen 		/* 0-align channel ID */
48987625b24SNicolin Chen 		ret = ina3221_write_enable(dev, channel - 1, val);
49087625b24SNicolin Chen 		break;
491d4b0166dSNicolin Chen 	case hwmon_curr:
49287625b24SNicolin Chen 		ret = ina3221_write_curr(dev, attr, channel, val);
49387625b24SNicolin Chen 		break;
494d4b0166dSNicolin Chen 	default:
49587625b24SNicolin Chen 		ret = -EOPNOTSUPP;
49687625b24SNicolin Chen 		break;
497d4b0166dSNicolin Chen 	}
49887625b24SNicolin Chen 
49987625b24SNicolin Chen 	mutex_unlock(&ina->lock);
50087625b24SNicolin Chen 
50187625b24SNicolin Chen 	return ret;
502d4b0166dSNicolin Chen }
503d4b0166dSNicolin Chen 
504d4b0166dSNicolin Chen static int ina3221_read_string(struct device *dev, enum hwmon_sensor_types type,
505d4b0166dSNicolin Chen 			       u32 attr, int channel, const char **str)
506d4b0166dSNicolin Chen {
507d4b0166dSNicolin Chen 	struct ina3221_data *ina = dev_get_drvdata(dev);
508d4b0166dSNicolin Chen 	int index = channel - 1;
509d4b0166dSNicolin Chen 
510d4b0166dSNicolin Chen 	*str = ina->inputs[index].label;
511d4b0166dSNicolin Chen 
512d4b0166dSNicolin Chen 	return 0;
513d4b0166dSNicolin Chen }
514d4b0166dSNicolin Chen 
515d4b0166dSNicolin Chen static umode_t ina3221_is_visible(const void *drvdata,
516d4b0166dSNicolin Chen 				  enum hwmon_sensor_types type,
517d4b0166dSNicolin Chen 				  u32 attr, int channel)
518d4b0166dSNicolin Chen {
519d4b0166dSNicolin Chen 	const struct ina3221_data *ina = drvdata;
520d4b0166dSNicolin Chen 	const struct ina3221_input *input = NULL;
521d4b0166dSNicolin Chen 
522d4b0166dSNicolin Chen 	switch (type) {
5235c090abfSNicolin Chen 	case hwmon_chip:
5245c090abfSNicolin Chen 		switch (attr) {
5255c090abfSNicolin Chen 		case hwmon_chip_samples:
526023912dbSNicolin Chen 		case hwmon_chip_update_interval:
5275c090abfSNicolin Chen 			return 0644;
5285c090abfSNicolin Chen 		default:
5295c090abfSNicolin Chen 			return 0;
5305c090abfSNicolin Chen 		}
531d4b0166dSNicolin Chen 	case hwmon_in:
532d4b0166dSNicolin Chen 		/* Ignore in0_ */
533d4b0166dSNicolin Chen 		if (channel == 0)
534d4b0166dSNicolin Chen 			return 0;
535d4b0166dSNicolin Chen 
536d4b0166dSNicolin Chen 		switch (attr) {
537d4b0166dSNicolin Chen 		case hwmon_in_label:
538d4b0166dSNicolin Chen 			if (channel - 1 <= INA3221_CHANNEL3)
539d4b0166dSNicolin Chen 				input = &ina->inputs[channel - 1];
540d4b0166dSNicolin Chen 			/* Hide label node if label is not provided */
541d4b0166dSNicolin Chen 			return (input && input->label) ? 0444 : 0;
542d4b0166dSNicolin Chen 		case hwmon_in_input:
543d4b0166dSNicolin Chen 			return 0444;
544d4b0166dSNicolin Chen 		case hwmon_in_enable:
545d4b0166dSNicolin Chen 			return 0644;
546d4b0166dSNicolin Chen 		default:
547d4b0166dSNicolin Chen 			return 0;
548d4b0166dSNicolin Chen 		}
549d4b0166dSNicolin Chen 	case hwmon_curr:
550d4b0166dSNicolin Chen 		switch (attr) {
551d4b0166dSNicolin Chen 		case hwmon_curr_input:
552d4b0166dSNicolin Chen 		case hwmon_curr_crit_alarm:
553d4b0166dSNicolin Chen 		case hwmon_curr_max_alarm:
554d4b0166dSNicolin Chen 			return 0444;
555d4b0166dSNicolin Chen 		case hwmon_curr_crit:
556d4b0166dSNicolin Chen 		case hwmon_curr_max:
557d4b0166dSNicolin Chen 			return 0644;
558d4b0166dSNicolin Chen 		default:
559d4b0166dSNicolin Chen 			return 0;
560d4b0166dSNicolin Chen 		}
561d4b0166dSNicolin Chen 	default:
562d4b0166dSNicolin Chen 		return 0;
563d4b0166dSNicolin Chen 	}
564d4b0166dSNicolin Chen }
565d4b0166dSNicolin Chen 
5666f307b7cSGuenter Roeck #define INA3221_HWMON_CURR_CONFIG (HWMON_C_INPUT | \
5676f307b7cSGuenter Roeck 				   HWMON_C_CRIT | HWMON_C_CRIT_ALARM | \
5686f307b7cSGuenter Roeck 				   HWMON_C_MAX | HWMON_C_MAX_ALARM)
5696f307b7cSGuenter Roeck 
5706f307b7cSGuenter Roeck static const struct hwmon_channel_info *ina3221_info[] = {
5715c090abfSNicolin Chen 	HWMON_CHANNEL_INFO(chip,
572023912dbSNicolin Chen 			   HWMON_C_SAMPLES,
573023912dbSNicolin Chen 			   HWMON_C_UPDATE_INTERVAL),
5746f307b7cSGuenter Roeck 	HWMON_CHANNEL_INFO(in,
575d4b0166dSNicolin Chen 			   /* 0: dummy, skipped in is_visible */
576d4b0166dSNicolin Chen 			   HWMON_I_INPUT,
577d4b0166dSNicolin Chen 			   /* 1-3: input voltage Channels */
578d4b0166dSNicolin Chen 			   HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL,
579d4b0166dSNicolin Chen 			   HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL,
580d4b0166dSNicolin Chen 			   HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL,
581d4b0166dSNicolin Chen 			   /* 4-6: shunt voltage Channels */
582d4b0166dSNicolin Chen 			   HWMON_I_INPUT,
583d4b0166dSNicolin Chen 			   HWMON_I_INPUT,
5846f307b7cSGuenter Roeck 			   HWMON_I_INPUT),
5856f307b7cSGuenter Roeck 	HWMON_CHANNEL_INFO(curr,
586d4b0166dSNicolin Chen 			   INA3221_HWMON_CURR_CONFIG,
587d4b0166dSNicolin Chen 			   INA3221_HWMON_CURR_CONFIG,
5886f307b7cSGuenter Roeck 			   INA3221_HWMON_CURR_CONFIG),
589d4b0166dSNicolin Chen 	NULL
590d4b0166dSNicolin Chen };
591d4b0166dSNicolin Chen 
592d4b0166dSNicolin Chen static const struct hwmon_ops ina3221_hwmon_ops = {
593d4b0166dSNicolin Chen 	.is_visible = ina3221_is_visible,
594d4b0166dSNicolin Chen 	.read_string = ina3221_read_string,
595d4b0166dSNicolin Chen 	.read = ina3221_read,
596d4b0166dSNicolin Chen 	.write = ina3221_write,
597d4b0166dSNicolin Chen };
598d4b0166dSNicolin Chen 
599d4b0166dSNicolin Chen static const struct hwmon_chip_info ina3221_chip_info = {
600d4b0166dSNicolin Chen 	.ops = &ina3221_hwmon_ops,
601d4b0166dSNicolin Chen 	.info = ina3221_info,
602d4b0166dSNicolin Chen };
603d4b0166dSNicolin Chen 
604d4b0166dSNicolin Chen /* Extra attribute groups */
605a4ec92edSGuenter Roeck static ssize_t ina3221_shunt_show(struct device *dev,
6067cb6dcffSAndrew F. Davis 				  struct device_attribute *attr, char *buf)
6077cb6dcffSAndrew F. Davis {
6087cb6dcffSAndrew F. Davis 	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
6097cb6dcffSAndrew F. Davis 	struct ina3221_data *ina = dev_get_drvdata(dev);
6107cb6dcffSAndrew F. Davis 	unsigned int channel = sd_attr->index;
611a9e9dd9cSNicolin Chen 	struct ina3221_input *input = &ina->inputs[channel];
6127cb6dcffSAndrew F. Davis 
613a9e9dd9cSNicolin Chen 	return snprintf(buf, PAGE_SIZE, "%d\n", input->shunt_resistor);
6147cb6dcffSAndrew F. Davis }
6157cb6dcffSAndrew F. Davis 
616a4ec92edSGuenter Roeck static ssize_t ina3221_shunt_store(struct device *dev,
6177cb6dcffSAndrew F. Davis 				   struct device_attribute *attr,
6187cb6dcffSAndrew F. Davis 				   const char *buf, size_t count)
6197cb6dcffSAndrew F. Davis {
6207cb6dcffSAndrew F. Davis 	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
6217cb6dcffSAndrew F. Davis 	struct ina3221_data *ina = dev_get_drvdata(dev);
6227cb6dcffSAndrew F. Davis 	unsigned int channel = sd_attr->index;
623a9e9dd9cSNicolin Chen 	struct ina3221_input *input = &ina->inputs[channel];
6249ad0df1aSGuenter Roeck 	int val;
6257cb6dcffSAndrew F. Davis 	int ret;
6267cb6dcffSAndrew F. Davis 
6279ad0df1aSGuenter Roeck 	ret = kstrtoint(buf, 0, &val);
6287cb6dcffSAndrew F. Davis 	if (ret)
6297cb6dcffSAndrew F. Davis 		return ret;
6307cb6dcffSAndrew F. Davis 
6319ad0df1aSGuenter Roeck 	val = clamp_val(val, 1, INT_MAX);
6327cb6dcffSAndrew F. Davis 
633a9e9dd9cSNicolin Chen 	input->shunt_resistor = val;
6347cb6dcffSAndrew F. Davis 
6357cb6dcffSAndrew F. Davis 	return count;
6367cb6dcffSAndrew F. Davis }
6377cb6dcffSAndrew F. Davis 
6387cb6dcffSAndrew F. Davis /* shunt resistance */
639a4ec92edSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(shunt1_resistor, ina3221_shunt, INA3221_CHANNEL1);
640a4ec92edSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(shunt2_resistor, ina3221_shunt, INA3221_CHANNEL2);
641a4ec92edSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(shunt3_resistor, ina3221_shunt, INA3221_CHANNEL3);
6427cb6dcffSAndrew F. Davis 
6437cb6dcffSAndrew F. Davis static struct attribute *ina3221_attrs[] = {
6447cb6dcffSAndrew F. Davis 	&sensor_dev_attr_shunt1_resistor.dev_attr.attr,
6457cb6dcffSAndrew F. Davis 	&sensor_dev_attr_shunt2_resistor.dev_attr.attr,
6467cb6dcffSAndrew F. Davis 	&sensor_dev_attr_shunt3_resistor.dev_attr.attr,
6477cb6dcffSAndrew F. Davis 	NULL,
6487cb6dcffSAndrew F. Davis };
649d4b0166dSNicolin Chen ATTRIBUTE_GROUPS(ina3221);
6507cb6dcffSAndrew F. Davis 
6517cb6dcffSAndrew F. Davis static const struct regmap_range ina3221_yes_ranges[] = {
652c20217b3SNicolin Chen 	regmap_reg_range(INA3221_CONFIG, INA3221_BUS3),
6537cb6dcffSAndrew F. Davis 	regmap_reg_range(INA3221_MASK_ENABLE, INA3221_MASK_ENABLE),
6547cb6dcffSAndrew F. Davis };
6557cb6dcffSAndrew F. Davis 
6567cb6dcffSAndrew F. Davis static const struct regmap_access_table ina3221_volatile_table = {
6577cb6dcffSAndrew F. Davis 	.yes_ranges = ina3221_yes_ranges,
6587cb6dcffSAndrew F. Davis 	.n_yes_ranges = ARRAY_SIZE(ina3221_yes_ranges),
6597cb6dcffSAndrew F. Davis };
6607cb6dcffSAndrew F. Davis 
6617cb6dcffSAndrew F. Davis static const struct regmap_config ina3221_regmap_config = {
6627cb6dcffSAndrew F. Davis 	.reg_bits = 8,
6637cb6dcffSAndrew F. Davis 	.val_bits = 16,
6647cb6dcffSAndrew F. Davis 
6657cb6dcffSAndrew F. Davis 	.cache_type = REGCACHE_RBTREE,
6667cb6dcffSAndrew F. Davis 	.volatile_table = &ina3221_volatile_table,
6677cb6dcffSAndrew F. Davis };
6687cb6dcffSAndrew F. Davis 
669a9e9dd9cSNicolin Chen static int ina3221_probe_child_from_dt(struct device *dev,
670a9e9dd9cSNicolin Chen 				       struct device_node *child,
671a9e9dd9cSNicolin Chen 				       struct ina3221_data *ina)
672a9e9dd9cSNicolin Chen {
673a9e9dd9cSNicolin Chen 	struct ina3221_input *input;
674a9e9dd9cSNicolin Chen 	u32 val;
675a9e9dd9cSNicolin Chen 	int ret;
676a9e9dd9cSNicolin Chen 
677a9e9dd9cSNicolin Chen 	ret = of_property_read_u32(child, "reg", &val);
678a9e9dd9cSNicolin Chen 	if (ret) {
6791b1f4efaSRob Herring 		dev_err(dev, "missing reg property of %pOFn\n", child);
680a9e9dd9cSNicolin Chen 		return ret;
681a9e9dd9cSNicolin Chen 	} else if (val > INA3221_CHANNEL3) {
6821b1f4efaSRob Herring 		dev_err(dev, "invalid reg %d of %pOFn\n", val, child);
683a9e9dd9cSNicolin Chen 		return ret;
684a9e9dd9cSNicolin Chen 	}
685a9e9dd9cSNicolin Chen 
686a9e9dd9cSNicolin Chen 	input = &ina->inputs[val];
687a9e9dd9cSNicolin Chen 
688a9e9dd9cSNicolin Chen 	/* Log the disconnected channel input */
689a9e9dd9cSNicolin Chen 	if (!of_device_is_available(child)) {
690a9e9dd9cSNicolin Chen 		input->disconnected = true;
691a9e9dd9cSNicolin Chen 		return 0;
692a9e9dd9cSNicolin Chen 	}
693a9e9dd9cSNicolin Chen 
694a9e9dd9cSNicolin Chen 	/* Save the connected input label if available */
695a9e9dd9cSNicolin Chen 	of_property_read_string(child, "label", &input->label);
696a9e9dd9cSNicolin Chen 
697a9e9dd9cSNicolin Chen 	/* Overwrite default shunt resistor value optionally */
698a6e43263SNicolin Chen 	if (!of_property_read_u32(child, "shunt-resistor-micro-ohms", &val)) {
699a6e43263SNicolin Chen 		if (val < 1 || val > INT_MAX) {
7001b1f4efaSRob Herring 			dev_err(dev, "invalid shunt resistor value %u of %pOFn\n",
7011b1f4efaSRob Herring 				val, child);
702a6e43263SNicolin Chen 			return -EINVAL;
703a6e43263SNicolin Chen 		}
704a9e9dd9cSNicolin Chen 		input->shunt_resistor = val;
705a6e43263SNicolin Chen 	}
706a9e9dd9cSNicolin Chen 
707a9e9dd9cSNicolin Chen 	return 0;
708a9e9dd9cSNicolin Chen }
709a9e9dd9cSNicolin Chen 
710a9e9dd9cSNicolin Chen static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina)
711a9e9dd9cSNicolin Chen {
712a9e9dd9cSNicolin Chen 	const struct device_node *np = dev->of_node;
713a9e9dd9cSNicolin Chen 	struct device_node *child;
714a9e9dd9cSNicolin Chen 	int ret;
715a9e9dd9cSNicolin Chen 
716a9e9dd9cSNicolin Chen 	/* Compatible with non-DT platforms */
717a9e9dd9cSNicolin Chen 	if (!np)
718a9e9dd9cSNicolin Chen 		return 0;
719a9e9dd9cSNicolin Chen 
72043dece16SNicolin Chen 	ina->single_shot = of_property_read_bool(np, "ti,single-shot");
72143dece16SNicolin Chen 
722a9e9dd9cSNicolin Chen 	for_each_child_of_node(np, child) {
723a9e9dd9cSNicolin Chen 		ret = ina3221_probe_child_from_dt(dev, child, ina);
724a9e9dd9cSNicolin Chen 		if (ret)
725a9e9dd9cSNicolin Chen 			return ret;
726a9e9dd9cSNicolin Chen 	}
727a9e9dd9cSNicolin Chen 
728a9e9dd9cSNicolin Chen 	return 0;
729a9e9dd9cSNicolin Chen }
730a9e9dd9cSNicolin Chen 
7317cb6dcffSAndrew F. Davis static int ina3221_probe(struct i2c_client *client,
7327cb6dcffSAndrew F. Davis 			 const struct i2c_device_id *id)
7337cb6dcffSAndrew F. Davis {
7347cb6dcffSAndrew F. Davis 	struct device *dev = &client->dev;
7357cb6dcffSAndrew F. Davis 	struct ina3221_data *ina;
7367cb6dcffSAndrew F. Davis 	struct device *hwmon_dev;
7377cb6dcffSAndrew F. Davis 	int i, ret;
7387cb6dcffSAndrew F. Davis 
7397cb6dcffSAndrew F. Davis 	ina = devm_kzalloc(dev, sizeof(*ina), GFP_KERNEL);
7407cb6dcffSAndrew F. Davis 	if (!ina)
7417cb6dcffSAndrew F. Davis 		return -ENOMEM;
7427cb6dcffSAndrew F. Davis 
7437cb6dcffSAndrew F. Davis 	ina->regmap = devm_regmap_init_i2c(client, &ina3221_regmap_config);
7447cb6dcffSAndrew F. Davis 	if (IS_ERR(ina->regmap)) {
7457cb6dcffSAndrew F. Davis 		dev_err(dev, "Unable to allocate register map\n");
7467cb6dcffSAndrew F. Davis 		return PTR_ERR(ina->regmap);
7477cb6dcffSAndrew F. Davis 	}
7487cb6dcffSAndrew F. Davis 
7497cb6dcffSAndrew F. Davis 	for (i = 0; i < F_MAX_FIELDS; i++) {
7507cb6dcffSAndrew F. Davis 		ina->fields[i] = devm_regmap_field_alloc(dev,
7517cb6dcffSAndrew F. Davis 							 ina->regmap,
7527cb6dcffSAndrew F. Davis 							 ina3221_reg_fields[i]);
7537cb6dcffSAndrew F. Davis 		if (IS_ERR(ina->fields[i])) {
7547cb6dcffSAndrew F. Davis 			dev_err(dev, "Unable to allocate regmap fields\n");
7557cb6dcffSAndrew F. Davis 			return PTR_ERR(ina->fields[i]);
7567cb6dcffSAndrew F. Davis 		}
7577cb6dcffSAndrew F. Davis 	}
7587cb6dcffSAndrew F. Davis 
7597cb6dcffSAndrew F. Davis 	for (i = 0; i < INA3221_NUM_CHANNELS; i++)
760a9e9dd9cSNicolin Chen 		ina->inputs[i].shunt_resistor = INA3221_RSHUNT_DEFAULT;
761a9e9dd9cSNicolin Chen 
762a9e9dd9cSNicolin Chen 	ret = ina3221_probe_from_dt(dev, ina);
763a9e9dd9cSNicolin Chen 	if (ret) {
764a9e9dd9cSNicolin Chen 		dev_err(dev, "Unable to probe from device tree\n");
765a9e9dd9cSNicolin Chen 		return ret;
766a9e9dd9cSNicolin Chen 	}
7677cb6dcffSAndrew F. Davis 
768323aeb0eSNicolin Chen 	/* The driver will be reset, so use reset value */
769323aeb0eSNicolin Chen 	ina->reg_config = INA3221_CONFIG_DEFAULT;
770a9e9dd9cSNicolin Chen 
77143dece16SNicolin Chen 	/* Clear continuous bit to use single-shot mode */
77243dece16SNicolin Chen 	if (ina->single_shot)
77343dece16SNicolin Chen 		ina->reg_config &= ~INA3221_CONFIG_MODE_CONTINUOUS;
77443dece16SNicolin Chen 
775a9e9dd9cSNicolin Chen 	/* Disable channels if their inputs are disconnected */
776a9e9dd9cSNicolin Chen 	for (i = 0; i < INA3221_NUM_CHANNELS; i++) {
777a9e9dd9cSNicolin Chen 		if (ina->inputs[i].disconnected)
778a9e9dd9cSNicolin Chen 			ina->reg_config &= ~INA3221_CONFIG_CHx_EN(i);
779a9e9dd9cSNicolin Chen 	}
780a9e9dd9cSNicolin Chen 
781323aeb0eSNicolin Chen 	ina->pm_dev = dev;
78287625b24SNicolin Chen 	mutex_init(&ina->lock);
78359d608e1SNicolin Chen 	dev_set_drvdata(dev, ina);
78459d608e1SNicolin Chen 
785323aeb0eSNicolin Chen 	/* Enable PM runtime -- status is suspended by default */
786323aeb0eSNicolin Chen 	pm_runtime_enable(ina->pm_dev);
787323aeb0eSNicolin Chen 
788323aeb0eSNicolin Chen 	/* Initialize (resume) the device */
789323aeb0eSNicolin Chen 	for (i = 0; i < INA3221_NUM_CHANNELS; i++) {
790323aeb0eSNicolin Chen 		if (ina->inputs[i].disconnected)
791323aeb0eSNicolin Chen 			continue;
792323aeb0eSNicolin Chen 		/* Match the refcount with number of enabled channels */
793323aeb0eSNicolin Chen 		ret = pm_runtime_get_sync(ina->pm_dev);
794323aeb0eSNicolin Chen 		if (ret < 0)
795323aeb0eSNicolin Chen 			goto fail;
796323aeb0eSNicolin Chen 	}
797323aeb0eSNicolin Chen 
798d4b0166dSNicolin Chen 	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, ina,
799d4b0166dSNicolin Chen 							 &ina3221_chip_info,
800d4b0166dSNicolin Chen 							 ina3221_groups);
8017cb6dcffSAndrew F. Davis 	if (IS_ERR(hwmon_dev)) {
8027cb6dcffSAndrew F. Davis 		dev_err(dev, "Unable to register hwmon device\n");
803323aeb0eSNicolin Chen 		ret = PTR_ERR(hwmon_dev);
804323aeb0eSNicolin Chen 		goto fail;
8057cb6dcffSAndrew F. Davis 	}
8067cb6dcffSAndrew F. Davis 
8077cb6dcffSAndrew F. Davis 	return 0;
808323aeb0eSNicolin Chen 
809323aeb0eSNicolin Chen fail:
810323aeb0eSNicolin Chen 	pm_runtime_disable(ina->pm_dev);
811323aeb0eSNicolin Chen 	pm_runtime_set_suspended(ina->pm_dev);
812323aeb0eSNicolin Chen 	/* pm_runtime_put_noidle() will decrease the PM refcount until 0 */
813323aeb0eSNicolin Chen 	for (i = 0; i < INA3221_NUM_CHANNELS; i++)
814323aeb0eSNicolin Chen 		pm_runtime_put_noidle(ina->pm_dev);
815323aeb0eSNicolin Chen 	mutex_destroy(&ina->lock);
816323aeb0eSNicolin Chen 
817323aeb0eSNicolin Chen 	return ret;
8187cb6dcffSAndrew F. Davis }
8197cb6dcffSAndrew F. Davis 
82087625b24SNicolin Chen static int ina3221_remove(struct i2c_client *client)
82187625b24SNicolin Chen {
82287625b24SNicolin Chen 	struct ina3221_data *ina = dev_get_drvdata(&client->dev);
823323aeb0eSNicolin Chen 	int i;
824323aeb0eSNicolin Chen 
825323aeb0eSNicolin Chen 	pm_runtime_disable(ina->pm_dev);
826323aeb0eSNicolin Chen 	pm_runtime_set_suspended(ina->pm_dev);
827323aeb0eSNicolin Chen 
828323aeb0eSNicolin Chen 	/* pm_runtime_put_noidle() will decrease the PM refcount until 0 */
829323aeb0eSNicolin Chen 	for (i = 0; i < INA3221_NUM_CHANNELS; i++)
830323aeb0eSNicolin Chen 		pm_runtime_put_noidle(ina->pm_dev);
83187625b24SNicolin Chen 
83287625b24SNicolin Chen 	mutex_destroy(&ina->lock);
83387625b24SNicolin Chen 
83487625b24SNicolin Chen 	return 0;
83587625b24SNicolin Chen }
83687625b24SNicolin Chen 
837ead21c77SArnd Bergmann static int __maybe_unused ina3221_suspend(struct device *dev)
83859d608e1SNicolin Chen {
83959d608e1SNicolin Chen 	struct ina3221_data *ina = dev_get_drvdata(dev);
84059d608e1SNicolin Chen 	int ret;
84159d608e1SNicolin Chen 
84259d608e1SNicolin Chen 	/* Save config register value and enable cache-only */
84359d608e1SNicolin Chen 	ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config);
84459d608e1SNicolin Chen 	if (ret)
84559d608e1SNicolin Chen 		return ret;
84659d608e1SNicolin Chen 
84759d608e1SNicolin Chen 	/* Set to power-down mode for power saving */
84859d608e1SNicolin Chen 	ret = regmap_update_bits(ina->regmap, INA3221_CONFIG,
84959d608e1SNicolin Chen 				 INA3221_CONFIG_MODE_MASK,
85059d608e1SNicolin Chen 				 INA3221_CONFIG_MODE_POWERDOWN);
85159d608e1SNicolin Chen 	if (ret)
85259d608e1SNicolin Chen 		return ret;
85359d608e1SNicolin Chen 
85459d608e1SNicolin Chen 	regcache_cache_only(ina->regmap, true);
85559d608e1SNicolin Chen 	regcache_mark_dirty(ina->regmap);
85659d608e1SNicolin Chen 
85759d608e1SNicolin Chen 	return 0;
85859d608e1SNicolin Chen }
85959d608e1SNicolin Chen 
860ead21c77SArnd Bergmann static int __maybe_unused ina3221_resume(struct device *dev)
86159d608e1SNicolin Chen {
86259d608e1SNicolin Chen 	struct ina3221_data *ina = dev_get_drvdata(dev);
86359d608e1SNicolin Chen 	int ret;
86459d608e1SNicolin Chen 
86559d608e1SNicolin Chen 	regcache_cache_only(ina->regmap, false);
86659d608e1SNicolin Chen 
86759d608e1SNicolin Chen 	/* Software reset the chip */
86859d608e1SNicolin Chen 	ret = regmap_field_write(ina->fields[F_RST], true);
86959d608e1SNicolin Chen 	if (ret) {
87059d608e1SNicolin Chen 		dev_err(dev, "Unable to reset device\n");
87159d608e1SNicolin Chen 		return ret;
87259d608e1SNicolin Chen 	}
87359d608e1SNicolin Chen 
87459d608e1SNicolin Chen 	/* Restore cached register values to hardware */
87559d608e1SNicolin Chen 	ret = regcache_sync(ina->regmap);
87659d608e1SNicolin Chen 	if (ret)
87759d608e1SNicolin Chen 		return ret;
87859d608e1SNicolin Chen 
87959d608e1SNicolin Chen 	/* Restore config register value to hardware */
88059d608e1SNicolin Chen 	ret = regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config);
88159d608e1SNicolin Chen 	if (ret)
88259d608e1SNicolin Chen 		return ret;
88359d608e1SNicolin Chen 
88459d608e1SNicolin Chen 	return 0;
88559d608e1SNicolin Chen }
88659d608e1SNicolin Chen 
88759d608e1SNicolin Chen static const struct dev_pm_ops ina3221_pm = {
888323aeb0eSNicolin Chen 	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
889323aeb0eSNicolin Chen 				pm_runtime_force_resume)
890323aeb0eSNicolin Chen 	SET_RUNTIME_PM_OPS(ina3221_suspend, ina3221_resume, NULL)
89159d608e1SNicolin Chen };
89259d608e1SNicolin Chen 
8937cb6dcffSAndrew F. Davis static const struct of_device_id ina3221_of_match_table[] = {
8947cb6dcffSAndrew F. Davis 	{ .compatible = "ti,ina3221", },
8957cb6dcffSAndrew F. Davis 	{ /* sentinel */ }
8967cb6dcffSAndrew F. Davis };
8977cb6dcffSAndrew F. Davis MODULE_DEVICE_TABLE(of, ina3221_of_match_table);
8987cb6dcffSAndrew F. Davis 
8997cb6dcffSAndrew F. Davis static const struct i2c_device_id ina3221_ids[] = {
9007cb6dcffSAndrew F. Davis 	{ "ina3221", 0 },
9017cb6dcffSAndrew F. Davis 	{ /* sentinel */ }
9027cb6dcffSAndrew F. Davis };
9037cb6dcffSAndrew F. Davis MODULE_DEVICE_TABLE(i2c, ina3221_ids);
9047cb6dcffSAndrew F. Davis 
9057cb6dcffSAndrew F. Davis static struct i2c_driver ina3221_i2c_driver = {
9067cb6dcffSAndrew F. Davis 	.probe = ina3221_probe,
90787625b24SNicolin Chen 	.remove = ina3221_remove,
9087cb6dcffSAndrew F. Davis 	.driver = {
9097cb6dcffSAndrew F. Davis 		.name = INA3221_DRIVER_NAME,
9107cb6dcffSAndrew F. Davis 		.of_match_table = ina3221_of_match_table,
91159d608e1SNicolin Chen 		.pm = &ina3221_pm,
9127cb6dcffSAndrew F. Davis 	},
9137cb6dcffSAndrew F. Davis 	.id_table = ina3221_ids,
9147cb6dcffSAndrew F. Davis };
9157cb6dcffSAndrew F. Davis module_i2c_driver(ina3221_i2c_driver);
9167cb6dcffSAndrew F. Davis 
9177cb6dcffSAndrew F. Davis MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
9187cb6dcffSAndrew F. Davis MODULE_DESCRIPTION("Texas Instruments INA3221 HWMon Driver");
9197cb6dcffSAndrew F. Davis MODULE_LICENSE("GPL v2");
920