xref: /openbmc/linux/drivers/hwmon/ina3221.c (revision 323aeb0e)
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>
257cb6dcffSAndrew F. Davis 
267cb6dcffSAndrew F. Davis #define INA3221_DRIVER_NAME		"ina3221"
277cb6dcffSAndrew F. Davis 
287cb6dcffSAndrew F. Davis #define INA3221_CONFIG			0x00
297cb6dcffSAndrew F. Davis #define INA3221_SHUNT1			0x01
307cb6dcffSAndrew F. Davis #define INA3221_BUS1			0x02
317cb6dcffSAndrew F. Davis #define INA3221_SHUNT2			0x03
327cb6dcffSAndrew F. Davis #define INA3221_BUS2			0x04
337cb6dcffSAndrew F. Davis #define INA3221_SHUNT3			0x05
347cb6dcffSAndrew F. Davis #define INA3221_BUS3			0x06
357cb6dcffSAndrew F. Davis #define INA3221_CRIT1			0x07
367cb6dcffSAndrew F. Davis #define INA3221_WARN1			0x08
377cb6dcffSAndrew F. Davis #define INA3221_CRIT2			0x09
387cb6dcffSAndrew F. Davis #define INA3221_WARN2			0x0a
397cb6dcffSAndrew F. Davis #define INA3221_CRIT3			0x0b
407cb6dcffSAndrew F. Davis #define INA3221_WARN3			0x0c
417cb6dcffSAndrew F. Davis #define INA3221_MASK_ENABLE		0x0f
427cb6dcffSAndrew F. Davis 
4359d608e1SNicolin Chen #define INA3221_CONFIG_MODE_MASK	GENMASK(2, 0)
4459d608e1SNicolin Chen #define INA3221_CONFIG_MODE_POWERDOWN	0
45791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_SHUNT	BIT(0)
46791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_BUS		BIT(1)
47791ebc9dSNicolin Chen #define INA3221_CONFIG_MODE_CONTINUOUS	BIT(2)
484c0415a3SNicolin Chen #define INA3221_CONFIG_VSH_CT_SHIFT	3
494c0415a3SNicolin Chen #define INA3221_CONFIG_VSH_CT_MASK	GENMASK(5, 3)
504c0415a3SNicolin Chen #define INA3221_CONFIG_VSH_CT(x)	(((x) & GENMASK(5, 3)) >> 3)
514c0415a3SNicolin Chen #define INA3221_CONFIG_VBUS_CT_SHIFT	6
524c0415a3SNicolin Chen #define INA3221_CONFIG_VBUS_CT_MASK	GENMASK(8, 6)
534c0415a3SNicolin Chen #define INA3221_CONFIG_VBUS_CT(x)	(((x) & GENMASK(8, 6)) >> 6)
544c0415a3SNicolin Chen #define INA3221_CONFIG_CHs_EN_MASK	GENMASK(14, 12)
55a9e9dd9cSNicolin Chen #define INA3221_CONFIG_CHx_EN(x)	BIT(14 - (x))
567cb6dcffSAndrew F. Davis 
57323aeb0eSNicolin Chen #define INA3221_CONFIG_DEFAULT		0x7127
587cb6dcffSAndrew F. Davis #define INA3221_RSHUNT_DEFAULT		10000
597cb6dcffSAndrew F. Davis 
607cb6dcffSAndrew F. Davis enum ina3221_fields {
617cb6dcffSAndrew F. Davis 	/* Configuration */
627cb6dcffSAndrew F. Davis 	F_RST,
637cb6dcffSAndrew F. Davis 
644c0415a3SNicolin Chen 	/* Status Flags */
654c0415a3SNicolin Chen 	F_CVRF,
664c0415a3SNicolin Chen 
677cb6dcffSAndrew F. Davis 	/* Alert Flags */
687cb6dcffSAndrew F. Davis 	F_WF3, F_WF2, F_WF1,
697cb6dcffSAndrew F. Davis 	F_CF3, F_CF2, F_CF1,
707cb6dcffSAndrew F. Davis 
717cb6dcffSAndrew F. Davis 	/* sentinel */
727cb6dcffSAndrew F. Davis 	F_MAX_FIELDS
737cb6dcffSAndrew F. Davis };
747cb6dcffSAndrew F. Davis 
757cb6dcffSAndrew F. Davis static const struct reg_field ina3221_reg_fields[] = {
767cb6dcffSAndrew F. Davis 	[F_RST] = REG_FIELD(INA3221_CONFIG, 15, 15),
777cb6dcffSAndrew F. Davis 
784c0415a3SNicolin Chen 	[F_CVRF] = REG_FIELD(INA3221_MASK_ENABLE, 0, 0),
797cb6dcffSAndrew F. Davis 	[F_WF3] = REG_FIELD(INA3221_MASK_ENABLE, 3, 3),
807cb6dcffSAndrew F. Davis 	[F_WF2] = REG_FIELD(INA3221_MASK_ENABLE, 4, 4),
817cb6dcffSAndrew F. Davis 	[F_WF1] = REG_FIELD(INA3221_MASK_ENABLE, 5, 5),
827cb6dcffSAndrew F. Davis 	[F_CF3] = REG_FIELD(INA3221_MASK_ENABLE, 7, 7),
837cb6dcffSAndrew F. Davis 	[F_CF2] = REG_FIELD(INA3221_MASK_ENABLE, 8, 8),
847cb6dcffSAndrew F. Davis 	[F_CF1] = REG_FIELD(INA3221_MASK_ENABLE, 9, 9),
857cb6dcffSAndrew F. Davis };
867cb6dcffSAndrew F. Davis 
877cb6dcffSAndrew F. Davis enum ina3221_channels {
887cb6dcffSAndrew F. Davis 	INA3221_CHANNEL1,
897cb6dcffSAndrew F. Davis 	INA3221_CHANNEL2,
907cb6dcffSAndrew F. Davis 	INA3221_CHANNEL3,
917cb6dcffSAndrew F. Davis 	INA3221_NUM_CHANNELS
927cb6dcffSAndrew F. Davis };
937cb6dcffSAndrew F. Davis 
947cb6dcffSAndrew F. Davis /**
95a9e9dd9cSNicolin Chen  * struct ina3221_input - channel input source specific information
96a9e9dd9cSNicolin Chen  * @label: label of channel input source
97a9e9dd9cSNicolin Chen  * @shunt_resistor: shunt resistor value of channel input source
98a9e9dd9cSNicolin Chen  * @disconnected: connection status of channel input source
99a9e9dd9cSNicolin Chen  */
100a9e9dd9cSNicolin Chen struct ina3221_input {
101a9e9dd9cSNicolin Chen 	const char *label;
102a9e9dd9cSNicolin Chen 	int shunt_resistor;
103a9e9dd9cSNicolin Chen 	bool disconnected;
104a9e9dd9cSNicolin Chen };
105a9e9dd9cSNicolin Chen 
106a9e9dd9cSNicolin Chen /**
1077cb6dcffSAndrew F. Davis  * struct ina3221_data - device specific information
108323aeb0eSNicolin Chen  * @pm_dev: Device pointer for pm runtime
1097cb6dcffSAndrew F. Davis  * @regmap: Register map of the device
1107cb6dcffSAndrew F. Davis  * @fields: Register fields of the device
111a9e9dd9cSNicolin Chen  * @inputs: Array of channel input source specific structures
11287625b24SNicolin Chen  * @lock: mutex lock to serialize sysfs attribute accesses
11359d608e1SNicolin Chen  * @reg_config: Register value of INA3221_CONFIG
1147cb6dcffSAndrew F. Davis  */
1157cb6dcffSAndrew F. Davis struct ina3221_data {
116323aeb0eSNicolin Chen 	struct device *pm_dev;
1177cb6dcffSAndrew F. Davis 	struct regmap *regmap;
1187cb6dcffSAndrew F. Davis 	struct regmap_field *fields[F_MAX_FIELDS];
119a9e9dd9cSNicolin Chen 	struct ina3221_input inputs[INA3221_NUM_CHANNELS];
12087625b24SNicolin Chen 	struct mutex lock;
12159d608e1SNicolin Chen 	u32 reg_config;
1227cb6dcffSAndrew F. Davis };
1237cb6dcffSAndrew F. Davis 
124a9e9dd9cSNicolin Chen static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel)
125a9e9dd9cSNicolin Chen {
126323aeb0eSNicolin Chen 	return pm_runtime_active(ina->pm_dev) &&
127323aeb0eSNicolin Chen 	       (ina->reg_config & INA3221_CONFIG_CHx_EN(channel));
128a9e9dd9cSNicolin Chen }
129a9e9dd9cSNicolin Chen 
1304c0415a3SNicolin Chen /* Lookup table for Bus and Shunt conversion times in usec */
1314c0415a3SNicolin Chen static const u16 ina3221_conv_time[] = {
1324c0415a3SNicolin Chen 	140, 204, 332, 588, 1100, 2116, 4156, 8244,
1334c0415a3SNicolin Chen };
1344c0415a3SNicolin Chen 
1354c0415a3SNicolin Chen static inline int ina3221_wait_for_data(struct ina3221_data *ina)
1364c0415a3SNicolin Chen {
1374c0415a3SNicolin Chen 	u32 channels = hweight16(ina->reg_config & INA3221_CONFIG_CHs_EN_MASK);
1384c0415a3SNicolin Chen 	u32 vbus_ct_idx = INA3221_CONFIG_VBUS_CT(ina->reg_config);
1394c0415a3SNicolin Chen 	u32 vsh_ct_idx = INA3221_CONFIG_VSH_CT(ina->reg_config);
1404c0415a3SNicolin Chen 	u32 vbus_ct = ina3221_conv_time[vbus_ct_idx];
1414c0415a3SNicolin Chen 	u32 vsh_ct = ina3221_conv_time[vsh_ct_idx];
1424c0415a3SNicolin Chen 	u32 wait, cvrf;
1434c0415a3SNicolin Chen 
1444c0415a3SNicolin Chen 	/* Calculate total conversion time */
1454c0415a3SNicolin Chen 	wait = channels * (vbus_ct + vsh_ct);
1464c0415a3SNicolin Chen 
1474c0415a3SNicolin Chen 	/* Polling the CVRF bit to make sure read data is ready */
1484c0415a3SNicolin Chen 	return regmap_field_read_poll_timeout(ina->fields[F_CVRF],
1494c0415a3SNicolin Chen 					      cvrf, cvrf, wait, 100000);
1504c0415a3SNicolin Chen }
1514c0415a3SNicolin Chen 
1527cb6dcffSAndrew F. Davis static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg,
1537cb6dcffSAndrew F. Davis 			      int *val)
1547cb6dcffSAndrew F. Davis {
1557cb6dcffSAndrew F. Davis 	unsigned int regval;
1567cb6dcffSAndrew F. Davis 	int ret;
1577cb6dcffSAndrew F. Davis 
1587cb6dcffSAndrew F. Davis 	ret = regmap_read(ina->regmap, reg, &regval);
1597cb6dcffSAndrew F. Davis 	if (ret)
1607cb6dcffSAndrew F. Davis 		return ret;
1617cb6dcffSAndrew F. Davis 
1627cb6dcffSAndrew F. Davis 	*val = sign_extend32(regval >> 3, 12);
1637cb6dcffSAndrew F. Davis 
1647cb6dcffSAndrew F. Davis 	return 0;
1657cb6dcffSAndrew F. Davis }
1667cb6dcffSAndrew F. Davis 
167d4b0166dSNicolin Chen static const u8 ina3221_in_reg[] = {
168d4b0166dSNicolin Chen 	INA3221_BUS1,
169d4b0166dSNicolin Chen 	INA3221_BUS2,
170d4b0166dSNicolin Chen 	INA3221_BUS3,
171d4b0166dSNicolin Chen 	INA3221_SHUNT1,
172d4b0166dSNicolin Chen 	INA3221_SHUNT2,
173d4b0166dSNicolin Chen 	INA3221_SHUNT3,
174d4b0166dSNicolin Chen };
1757cb6dcffSAndrew F. Davis 
176d4b0166dSNicolin Chen static int ina3221_read_in(struct device *dev, u32 attr, int channel, long *val)
177d4b0166dSNicolin Chen {
178d4b0166dSNicolin Chen 	const bool is_shunt = channel > INA3221_CHANNEL3;
179d4b0166dSNicolin Chen 	struct ina3221_data *ina = dev_get_drvdata(dev);
180d4b0166dSNicolin Chen 	u8 reg = ina3221_in_reg[channel];
181d4b0166dSNicolin Chen 	int regval, ret;
182d4b0166dSNicolin Chen 
183d4b0166dSNicolin Chen 	/* Translate shunt channel index to sensor channel index */
184d4b0166dSNicolin Chen 	channel %= INA3221_NUM_CHANNELS;
185d4b0166dSNicolin Chen 
186d4b0166dSNicolin Chen 	switch (attr) {
187d4b0166dSNicolin Chen 	case hwmon_in_input:
188d4b0166dSNicolin Chen 		if (!ina3221_is_enabled(ina, channel))
189a9e9dd9cSNicolin Chen 			return -ENODATA;
190a9e9dd9cSNicolin Chen 
1914c0415a3SNicolin Chen 		ret = ina3221_wait_for_data(ina);
1924c0415a3SNicolin Chen 		if (ret)
1934c0415a3SNicolin Chen 			return ret;
1944c0415a3SNicolin Chen 
195d4b0166dSNicolin Chen 		ret = ina3221_read_value(ina, reg, &regval);
1967cb6dcffSAndrew F. Davis 		if (ret)
1977cb6dcffSAndrew F. Davis 			return ret;
1987cb6dcffSAndrew F. Davis 
199d4b0166dSNicolin Chen 		/*
200d4b0166dSNicolin Chen 		 * Scale of shunt voltage (uV): LSB is 40uV
201d4b0166dSNicolin Chen 		 * Scale of bus voltage (mV): LSB is 8mV
202d4b0166dSNicolin Chen 		 */
203d4b0166dSNicolin Chen 		*val = regval * (is_shunt ? 40 : 8);
204d4b0166dSNicolin Chen 		return 0;
205d4b0166dSNicolin Chen 	case hwmon_in_enable:
206d4b0166dSNicolin Chen 		*val = ina3221_is_enabled(ina, channel);
207d4b0166dSNicolin Chen 		return 0;
208d4b0166dSNicolin Chen 	default:
209d4b0166dSNicolin Chen 		return -EOPNOTSUPP;
210d4b0166dSNicolin Chen 	}
2117cb6dcffSAndrew F. Davis }
2127cb6dcffSAndrew F. Davis 
213d4b0166dSNicolin Chen static const u8 ina3221_curr_reg[][INA3221_NUM_CHANNELS] = {
214d4b0166dSNicolin Chen 	[hwmon_curr_input] = { INA3221_SHUNT1, INA3221_SHUNT2, INA3221_SHUNT3 },
215d4b0166dSNicolin Chen 	[hwmon_curr_max] = { INA3221_WARN1, INA3221_WARN2, INA3221_WARN3 },
216d4b0166dSNicolin Chen 	[hwmon_curr_crit] = { INA3221_CRIT1, INA3221_CRIT2, INA3221_CRIT3 },
217d4b0166dSNicolin Chen 	[hwmon_curr_max_alarm] = { F_WF1, F_WF2, F_WF3 },
218d4b0166dSNicolin Chen 	[hwmon_curr_crit_alarm] = { F_CF1, F_CF2, F_CF3 },
219d4b0166dSNicolin Chen };
220d4b0166dSNicolin Chen 
221d4b0166dSNicolin Chen static int ina3221_read_curr(struct device *dev, u32 attr,
222d4b0166dSNicolin Chen 			     int channel, long *val)
2237cb6dcffSAndrew F. Davis {
2247cb6dcffSAndrew F. Davis 	struct ina3221_data *ina = dev_get_drvdata(dev);
225a9e9dd9cSNicolin Chen 	struct ina3221_input *input = &ina->inputs[channel];
226a9e9dd9cSNicolin Chen 	int resistance_uo = input->shunt_resistor;
227d4b0166dSNicolin Chen 	u8 reg = ina3221_curr_reg[attr][channel];
228d4b0166dSNicolin Chen 	int regval, voltage_nv, ret;
2297cb6dcffSAndrew F. Davis 
230d4b0166dSNicolin Chen 	switch (attr) {
231d4b0166dSNicolin Chen 	case hwmon_curr_input:
232d4b0166dSNicolin Chen 		if (!ina3221_is_enabled(ina, channel))
233a9e9dd9cSNicolin Chen 			return -ENODATA;
2344c0415a3SNicolin Chen 
2354c0415a3SNicolin Chen 		ret = ina3221_wait_for_data(ina);
2364c0415a3SNicolin Chen 		if (ret)
2374c0415a3SNicolin Chen 			return ret;
2384c0415a3SNicolin Chen 
239d4b0166dSNicolin Chen 		/* fall through */
240d4b0166dSNicolin Chen 	case hwmon_curr_crit:
241d4b0166dSNicolin Chen 	case hwmon_curr_max:
242d4b0166dSNicolin Chen 		ret = ina3221_read_value(ina, reg, &regval);
2437cb6dcffSAndrew F. Davis 		if (ret)
2447cb6dcffSAndrew F. Davis 			return ret;
2457cb6dcffSAndrew F. Davis 
246d4b0166dSNicolin Chen 		/* Scale of shunt voltage: LSB is 40uV (40000nV) */
247d4b0166dSNicolin Chen 		voltage_nv = regval * 40000;
248d4b0166dSNicolin Chen 		/* Return current in mA */
249d4b0166dSNicolin Chen 		*val = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo);
250d4b0166dSNicolin Chen 		return 0;
251d4b0166dSNicolin Chen 	case hwmon_curr_crit_alarm:
252d4b0166dSNicolin Chen 	case hwmon_curr_max_alarm:
253efb0489eSNicolin Chen 		/* No actual register read if channel is disabled */
254efb0489eSNicolin Chen 		if (!ina3221_is_enabled(ina, channel)) {
255efb0489eSNicolin Chen 			/* Return 0 for alert flags */
256efb0489eSNicolin Chen 			*val = 0;
257efb0489eSNicolin Chen 			return 0;
258efb0489eSNicolin Chen 		}
259d4b0166dSNicolin Chen 		ret = regmap_field_read(ina->fields[reg], &regval);
260d4b0166dSNicolin Chen 		if (ret)
261d4b0166dSNicolin Chen 			return ret;
262d4b0166dSNicolin Chen 		*val = regval;
263d4b0166dSNicolin Chen 		return 0;
264d4b0166dSNicolin Chen 	default:
265d4b0166dSNicolin Chen 		return -EOPNOTSUPP;
266d4b0166dSNicolin Chen 	}
2677cb6dcffSAndrew F. Davis }
2687cb6dcffSAndrew F. Davis 
269d4b0166dSNicolin Chen static int ina3221_write_curr(struct device *dev, u32 attr,
270d4b0166dSNicolin Chen 			      int channel, long val)
2717cb6dcffSAndrew F. Davis {
2727cb6dcffSAndrew F. Davis 	struct ina3221_data *ina = dev_get_drvdata(dev);
273a9e9dd9cSNicolin Chen 	struct ina3221_input *input = &ina->inputs[channel];
274a9e9dd9cSNicolin Chen 	int resistance_uo = input->shunt_resistor;
275d4b0166dSNicolin Chen 	u8 reg = ina3221_curr_reg[attr][channel];
276d4b0166dSNicolin Chen 	int regval, current_ma, voltage_uv;
2777cb6dcffSAndrew F. Davis 
2787cb6dcffSAndrew F. Davis 	/* clamp current */
279d4b0166dSNicolin Chen 	current_ma = clamp_val(val,
2807cb6dcffSAndrew F. Davis 			       INT_MIN / resistance_uo,
2817cb6dcffSAndrew F. Davis 			       INT_MAX / resistance_uo);
2827cb6dcffSAndrew F. Davis 
2837cb6dcffSAndrew F. Davis 	voltage_uv = DIV_ROUND_CLOSEST(current_ma * resistance_uo, 1000);
2847cb6dcffSAndrew F. Davis 
2857cb6dcffSAndrew F. Davis 	/* clamp voltage */
2867cb6dcffSAndrew F. Davis 	voltage_uv = clamp_val(voltage_uv, -163800, 163800);
2877cb6dcffSAndrew F. Davis 
2887cb6dcffSAndrew F. Davis 	/* 1 / 40uV(scale) << 3(register shift) = 5 */
289d4b0166dSNicolin Chen 	regval = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8;
2907cb6dcffSAndrew F. Davis 
291d4b0166dSNicolin Chen 	return regmap_write(ina->regmap, reg, regval);
292d4b0166dSNicolin Chen }
293d4b0166dSNicolin Chen 
294d4b0166dSNicolin Chen static int ina3221_write_enable(struct device *dev, int channel, bool enable)
295d4b0166dSNicolin Chen {
296d4b0166dSNicolin Chen 	struct ina3221_data *ina = dev_get_drvdata(dev);
297d4b0166dSNicolin Chen 	u16 config, mask = INA3221_CONFIG_CHx_EN(channel);
298323aeb0eSNicolin Chen 	u16 config_old = ina->reg_config & mask;
299d4b0166dSNicolin Chen 	int ret;
300d4b0166dSNicolin Chen 
301d4b0166dSNicolin Chen 	config = enable ? mask : 0;
302d4b0166dSNicolin Chen 
303323aeb0eSNicolin Chen 	/* Bypass if enable status is not being changed */
304323aeb0eSNicolin Chen 	if (config_old == config)
305323aeb0eSNicolin Chen 		return 0;
306323aeb0eSNicolin Chen 
307323aeb0eSNicolin Chen 	/* For enabling routine, increase refcount and resume() at first */
308323aeb0eSNicolin Chen 	if (enable) {
309323aeb0eSNicolin Chen 		ret = pm_runtime_get_sync(ina->pm_dev);
310323aeb0eSNicolin Chen 		if (ret < 0) {
311323aeb0eSNicolin Chen 			dev_err(dev, "Failed to get PM runtime\n");
312323aeb0eSNicolin Chen 			return ret;
313323aeb0eSNicolin Chen 		}
314323aeb0eSNicolin Chen 	}
315323aeb0eSNicolin Chen 
316d4b0166dSNicolin Chen 	/* Enable or disable the channel */
317d4b0166dSNicolin Chen 	ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, mask, config);
3187cb6dcffSAndrew F. Davis 	if (ret)
319323aeb0eSNicolin Chen 		goto fail;
3207cb6dcffSAndrew F. Davis 
321d4b0166dSNicolin Chen 	/* Cache the latest config register value */
322d4b0166dSNicolin Chen 	ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config);
323d4b0166dSNicolin Chen 	if (ret)
324323aeb0eSNicolin Chen 		goto fail;
325323aeb0eSNicolin Chen 
326323aeb0eSNicolin Chen 	/* For disabling routine, decrease refcount or suspend() at last */
327323aeb0eSNicolin Chen 	if (!enable)
328323aeb0eSNicolin Chen 		pm_runtime_put_sync(ina->pm_dev);
329d4b0166dSNicolin Chen 
330d4b0166dSNicolin Chen 	return 0;
331323aeb0eSNicolin Chen 
332323aeb0eSNicolin Chen fail:
333323aeb0eSNicolin Chen 	if (enable) {
334323aeb0eSNicolin Chen 		dev_err(dev, "Failed to enable channel %d: error %d\n",
335323aeb0eSNicolin Chen 			channel, ret);
336323aeb0eSNicolin Chen 		pm_runtime_put_sync(ina->pm_dev);
337323aeb0eSNicolin Chen 	}
338323aeb0eSNicolin Chen 
339323aeb0eSNicolin Chen 	return ret;
3407cb6dcffSAndrew F. Davis }
3417cb6dcffSAndrew F. Davis 
342d4b0166dSNicolin Chen static int ina3221_read(struct device *dev, enum hwmon_sensor_types type,
343d4b0166dSNicolin Chen 			u32 attr, int channel, long *val)
344d4b0166dSNicolin Chen {
34587625b24SNicolin Chen 	struct ina3221_data *ina = dev_get_drvdata(dev);
34687625b24SNicolin Chen 	int ret;
34787625b24SNicolin Chen 
34887625b24SNicolin Chen 	mutex_lock(&ina->lock);
34987625b24SNicolin Chen 
350d4b0166dSNicolin Chen 	switch (type) {
351d4b0166dSNicolin Chen 	case hwmon_in:
352d4b0166dSNicolin Chen 		/* 0-align channel ID */
35387625b24SNicolin Chen 		ret = ina3221_read_in(dev, attr, channel - 1, val);
35487625b24SNicolin Chen 		break;
355d4b0166dSNicolin Chen 	case hwmon_curr:
35687625b24SNicolin Chen 		ret = ina3221_read_curr(dev, attr, channel, val);
35787625b24SNicolin Chen 		break;
358d4b0166dSNicolin Chen 	default:
35987625b24SNicolin Chen 		ret = -EOPNOTSUPP;
36087625b24SNicolin Chen 		break;
361d4b0166dSNicolin Chen 	}
36287625b24SNicolin Chen 
36387625b24SNicolin Chen 	mutex_unlock(&ina->lock);
36487625b24SNicolin Chen 
36587625b24SNicolin Chen 	return ret;
366d4b0166dSNicolin Chen }
367d4b0166dSNicolin Chen 
368d4b0166dSNicolin Chen static int ina3221_write(struct device *dev, enum hwmon_sensor_types type,
369d4b0166dSNicolin Chen 			 u32 attr, int channel, long val)
370d4b0166dSNicolin Chen {
37187625b24SNicolin Chen 	struct ina3221_data *ina = dev_get_drvdata(dev);
37287625b24SNicolin Chen 	int ret;
37387625b24SNicolin Chen 
37487625b24SNicolin Chen 	mutex_lock(&ina->lock);
37587625b24SNicolin Chen 
376d4b0166dSNicolin Chen 	switch (type) {
377d4b0166dSNicolin Chen 	case hwmon_in:
378d4b0166dSNicolin Chen 		/* 0-align channel ID */
37987625b24SNicolin Chen 		ret = ina3221_write_enable(dev, channel - 1, val);
38087625b24SNicolin Chen 		break;
381d4b0166dSNicolin Chen 	case hwmon_curr:
38287625b24SNicolin Chen 		ret = ina3221_write_curr(dev, attr, channel, val);
38387625b24SNicolin Chen 		break;
384d4b0166dSNicolin Chen 	default:
38587625b24SNicolin Chen 		ret = -EOPNOTSUPP;
38687625b24SNicolin Chen 		break;
387d4b0166dSNicolin Chen 	}
38887625b24SNicolin Chen 
38987625b24SNicolin Chen 	mutex_unlock(&ina->lock);
39087625b24SNicolin Chen 
39187625b24SNicolin Chen 	return ret;
392d4b0166dSNicolin Chen }
393d4b0166dSNicolin Chen 
394d4b0166dSNicolin Chen static int ina3221_read_string(struct device *dev, enum hwmon_sensor_types type,
395d4b0166dSNicolin Chen 			       u32 attr, int channel, const char **str)
396d4b0166dSNicolin Chen {
397d4b0166dSNicolin Chen 	struct ina3221_data *ina = dev_get_drvdata(dev);
398d4b0166dSNicolin Chen 	int index = channel - 1;
399d4b0166dSNicolin Chen 
400d4b0166dSNicolin Chen 	*str = ina->inputs[index].label;
401d4b0166dSNicolin Chen 
402d4b0166dSNicolin Chen 	return 0;
403d4b0166dSNicolin Chen }
404d4b0166dSNicolin Chen 
405d4b0166dSNicolin Chen static umode_t ina3221_is_visible(const void *drvdata,
406d4b0166dSNicolin Chen 				  enum hwmon_sensor_types type,
407d4b0166dSNicolin Chen 				  u32 attr, int channel)
408d4b0166dSNicolin Chen {
409d4b0166dSNicolin Chen 	const struct ina3221_data *ina = drvdata;
410d4b0166dSNicolin Chen 	const struct ina3221_input *input = NULL;
411d4b0166dSNicolin Chen 
412d4b0166dSNicolin Chen 	switch (type) {
413d4b0166dSNicolin Chen 	case hwmon_in:
414d4b0166dSNicolin Chen 		/* Ignore in0_ */
415d4b0166dSNicolin Chen 		if (channel == 0)
416d4b0166dSNicolin Chen 			return 0;
417d4b0166dSNicolin Chen 
418d4b0166dSNicolin Chen 		switch (attr) {
419d4b0166dSNicolin Chen 		case hwmon_in_label:
420d4b0166dSNicolin Chen 			if (channel - 1 <= INA3221_CHANNEL3)
421d4b0166dSNicolin Chen 				input = &ina->inputs[channel - 1];
422d4b0166dSNicolin Chen 			/* Hide label node if label is not provided */
423d4b0166dSNicolin Chen 			return (input && input->label) ? 0444 : 0;
424d4b0166dSNicolin Chen 		case hwmon_in_input:
425d4b0166dSNicolin Chen 			return 0444;
426d4b0166dSNicolin Chen 		case hwmon_in_enable:
427d4b0166dSNicolin Chen 			return 0644;
428d4b0166dSNicolin Chen 		default:
429d4b0166dSNicolin Chen 			return 0;
430d4b0166dSNicolin Chen 		}
431d4b0166dSNicolin Chen 	case hwmon_curr:
432d4b0166dSNicolin Chen 		switch (attr) {
433d4b0166dSNicolin Chen 		case hwmon_curr_input:
434d4b0166dSNicolin Chen 		case hwmon_curr_crit_alarm:
435d4b0166dSNicolin Chen 		case hwmon_curr_max_alarm:
436d4b0166dSNicolin Chen 			return 0444;
437d4b0166dSNicolin Chen 		case hwmon_curr_crit:
438d4b0166dSNicolin Chen 		case hwmon_curr_max:
439d4b0166dSNicolin Chen 			return 0644;
440d4b0166dSNicolin Chen 		default:
441d4b0166dSNicolin Chen 			return 0;
442d4b0166dSNicolin Chen 		}
443d4b0166dSNicolin Chen 	default:
444d4b0166dSNicolin Chen 		return 0;
445d4b0166dSNicolin Chen 	}
446d4b0166dSNicolin Chen }
447d4b0166dSNicolin Chen 
448d4b0166dSNicolin Chen static const u32 ina3221_in_config[] = {
449d4b0166dSNicolin Chen 	/* 0: dummy, skipped in is_visible */
450d4b0166dSNicolin Chen 	HWMON_I_INPUT,
451d4b0166dSNicolin Chen 	/* 1-3: input voltage Channels */
452d4b0166dSNicolin Chen 	HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL,
453d4b0166dSNicolin Chen 	HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL,
454d4b0166dSNicolin Chen 	HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL,
455d4b0166dSNicolin Chen 	/* 4-6: shunt voltage Channels */
456d4b0166dSNicolin Chen 	HWMON_I_INPUT,
457d4b0166dSNicolin Chen 	HWMON_I_INPUT,
458d4b0166dSNicolin Chen 	HWMON_I_INPUT,
459d4b0166dSNicolin Chen 	0
460d4b0166dSNicolin Chen };
461d4b0166dSNicolin Chen 
462d4b0166dSNicolin Chen static const struct hwmon_channel_info ina3221_in = {
463d4b0166dSNicolin Chen 	.type = hwmon_in,
464d4b0166dSNicolin Chen 	.config = ina3221_in_config,
465d4b0166dSNicolin Chen };
466d4b0166dSNicolin Chen 
467d4b0166dSNicolin Chen #define INA3221_HWMON_CURR_CONFIG (HWMON_C_INPUT | \
468d4b0166dSNicolin Chen 				   HWMON_C_CRIT | HWMON_C_CRIT_ALARM | \
469d4b0166dSNicolin Chen 				   HWMON_C_MAX | HWMON_C_MAX_ALARM)
470d4b0166dSNicolin Chen 
471d4b0166dSNicolin Chen static const u32 ina3221_curr_config[] = {
472d4b0166dSNicolin Chen 	INA3221_HWMON_CURR_CONFIG,
473d4b0166dSNicolin Chen 	INA3221_HWMON_CURR_CONFIG,
474d4b0166dSNicolin Chen 	INA3221_HWMON_CURR_CONFIG,
475d4b0166dSNicolin Chen 	0
476d4b0166dSNicolin Chen };
477d4b0166dSNicolin Chen 
478d4b0166dSNicolin Chen static const struct hwmon_channel_info ina3221_curr = {
479d4b0166dSNicolin Chen 	.type = hwmon_curr,
480d4b0166dSNicolin Chen 	.config = ina3221_curr_config,
481d4b0166dSNicolin Chen };
482d4b0166dSNicolin Chen 
483d4b0166dSNicolin Chen static const struct hwmon_channel_info *ina3221_info[] = {
484d4b0166dSNicolin Chen 	&ina3221_in,
485d4b0166dSNicolin Chen 	&ina3221_curr,
486d4b0166dSNicolin Chen 	NULL
487d4b0166dSNicolin Chen };
488d4b0166dSNicolin Chen 
489d4b0166dSNicolin Chen static const struct hwmon_ops ina3221_hwmon_ops = {
490d4b0166dSNicolin Chen 	.is_visible = ina3221_is_visible,
491d4b0166dSNicolin Chen 	.read_string = ina3221_read_string,
492d4b0166dSNicolin Chen 	.read = ina3221_read,
493d4b0166dSNicolin Chen 	.write = ina3221_write,
494d4b0166dSNicolin Chen };
495d4b0166dSNicolin Chen 
496d4b0166dSNicolin Chen static const struct hwmon_chip_info ina3221_chip_info = {
497d4b0166dSNicolin Chen 	.ops = &ina3221_hwmon_ops,
498d4b0166dSNicolin Chen 	.info = ina3221_info,
499d4b0166dSNicolin Chen };
500d4b0166dSNicolin Chen 
501d4b0166dSNicolin Chen /* Extra attribute groups */
5027cb6dcffSAndrew F. Davis static ssize_t ina3221_show_shunt(struct device *dev,
5037cb6dcffSAndrew F. Davis 				  struct device_attribute *attr, char *buf)
5047cb6dcffSAndrew F. Davis {
5057cb6dcffSAndrew F. Davis 	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
5067cb6dcffSAndrew F. Davis 	struct ina3221_data *ina = dev_get_drvdata(dev);
5077cb6dcffSAndrew F. Davis 	unsigned int channel = sd_attr->index;
508a9e9dd9cSNicolin Chen 	struct ina3221_input *input = &ina->inputs[channel];
5097cb6dcffSAndrew F. Davis 
510a9e9dd9cSNicolin Chen 	return snprintf(buf, PAGE_SIZE, "%d\n", input->shunt_resistor);
5117cb6dcffSAndrew F. Davis }
5127cb6dcffSAndrew F. Davis 
5137cb6dcffSAndrew F. Davis static ssize_t ina3221_set_shunt(struct device *dev,
5147cb6dcffSAndrew F. Davis 				 struct device_attribute *attr,
5157cb6dcffSAndrew F. Davis 				 const char *buf, size_t count)
5167cb6dcffSAndrew F. Davis {
5177cb6dcffSAndrew F. Davis 	struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr);
5187cb6dcffSAndrew F. Davis 	struct ina3221_data *ina = dev_get_drvdata(dev);
5197cb6dcffSAndrew F. Davis 	unsigned int channel = sd_attr->index;
520a9e9dd9cSNicolin Chen 	struct ina3221_input *input = &ina->inputs[channel];
5219ad0df1aSGuenter Roeck 	int val;
5227cb6dcffSAndrew F. Davis 	int ret;
5237cb6dcffSAndrew F. Davis 
5249ad0df1aSGuenter Roeck 	ret = kstrtoint(buf, 0, &val);
5257cb6dcffSAndrew F. Davis 	if (ret)
5267cb6dcffSAndrew F. Davis 		return ret;
5277cb6dcffSAndrew F. Davis 
5289ad0df1aSGuenter Roeck 	val = clamp_val(val, 1, INT_MAX);
5297cb6dcffSAndrew F. Davis 
530a9e9dd9cSNicolin Chen 	input->shunt_resistor = val;
5317cb6dcffSAndrew F. Davis 
5327cb6dcffSAndrew F. Davis 	return count;
5337cb6dcffSAndrew F. Davis }
5347cb6dcffSAndrew F. Davis 
5357cb6dcffSAndrew F. Davis /* shunt resistance */
5367cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(shunt1_resistor, S_IRUGO | S_IWUSR,
5377cb6dcffSAndrew F. Davis 		ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL1);
5387cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(shunt2_resistor, S_IRUGO | S_IWUSR,
5397cb6dcffSAndrew F. Davis 		ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL2);
5407cb6dcffSAndrew F. Davis static SENSOR_DEVICE_ATTR(shunt3_resistor, S_IRUGO | S_IWUSR,
5417cb6dcffSAndrew F. Davis 		ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL3);
5427cb6dcffSAndrew F. Davis 
5437cb6dcffSAndrew F. Davis static struct attribute *ina3221_attrs[] = {
5447cb6dcffSAndrew F. Davis 	&sensor_dev_attr_shunt1_resistor.dev_attr.attr,
5457cb6dcffSAndrew F. Davis 	&sensor_dev_attr_shunt2_resistor.dev_attr.attr,
5467cb6dcffSAndrew F. Davis 	&sensor_dev_attr_shunt3_resistor.dev_attr.attr,
5477cb6dcffSAndrew F. Davis 	NULL,
5487cb6dcffSAndrew F. Davis };
549d4b0166dSNicolin Chen ATTRIBUTE_GROUPS(ina3221);
5507cb6dcffSAndrew F. Davis 
5517cb6dcffSAndrew F. Davis static const struct regmap_range ina3221_yes_ranges[] = {
552c20217b3SNicolin Chen 	regmap_reg_range(INA3221_CONFIG, INA3221_BUS3),
5537cb6dcffSAndrew F. Davis 	regmap_reg_range(INA3221_MASK_ENABLE, INA3221_MASK_ENABLE),
5547cb6dcffSAndrew F. Davis };
5557cb6dcffSAndrew F. Davis 
5567cb6dcffSAndrew F. Davis static const struct regmap_access_table ina3221_volatile_table = {
5577cb6dcffSAndrew F. Davis 	.yes_ranges = ina3221_yes_ranges,
5587cb6dcffSAndrew F. Davis 	.n_yes_ranges = ARRAY_SIZE(ina3221_yes_ranges),
5597cb6dcffSAndrew F. Davis };
5607cb6dcffSAndrew F. Davis 
5617cb6dcffSAndrew F. Davis static const struct regmap_config ina3221_regmap_config = {
5627cb6dcffSAndrew F. Davis 	.reg_bits = 8,
5637cb6dcffSAndrew F. Davis 	.val_bits = 16,
5647cb6dcffSAndrew F. Davis 
5657cb6dcffSAndrew F. Davis 	.cache_type = REGCACHE_RBTREE,
5667cb6dcffSAndrew F. Davis 	.volatile_table = &ina3221_volatile_table,
5677cb6dcffSAndrew F. Davis };
5687cb6dcffSAndrew F. Davis 
569a9e9dd9cSNicolin Chen static int ina3221_probe_child_from_dt(struct device *dev,
570a9e9dd9cSNicolin Chen 				       struct device_node *child,
571a9e9dd9cSNicolin Chen 				       struct ina3221_data *ina)
572a9e9dd9cSNicolin Chen {
573a9e9dd9cSNicolin Chen 	struct ina3221_input *input;
574a9e9dd9cSNicolin Chen 	u32 val;
575a9e9dd9cSNicolin Chen 	int ret;
576a9e9dd9cSNicolin Chen 
577a9e9dd9cSNicolin Chen 	ret = of_property_read_u32(child, "reg", &val);
578a9e9dd9cSNicolin Chen 	if (ret) {
579a9e9dd9cSNicolin Chen 		dev_err(dev, "missing reg property of %s\n", child->name);
580a9e9dd9cSNicolin Chen 		return ret;
581a9e9dd9cSNicolin Chen 	} else if (val > INA3221_CHANNEL3) {
582a9e9dd9cSNicolin Chen 		dev_err(dev, "invalid reg %d of %s\n", val, child->name);
583a9e9dd9cSNicolin Chen 		return ret;
584a9e9dd9cSNicolin Chen 	}
585a9e9dd9cSNicolin Chen 
586a9e9dd9cSNicolin Chen 	input = &ina->inputs[val];
587a9e9dd9cSNicolin Chen 
588a9e9dd9cSNicolin Chen 	/* Log the disconnected channel input */
589a9e9dd9cSNicolin Chen 	if (!of_device_is_available(child)) {
590a9e9dd9cSNicolin Chen 		input->disconnected = true;
591a9e9dd9cSNicolin Chen 		return 0;
592a9e9dd9cSNicolin Chen 	}
593a9e9dd9cSNicolin Chen 
594a9e9dd9cSNicolin Chen 	/* Save the connected input label if available */
595a9e9dd9cSNicolin Chen 	of_property_read_string(child, "label", &input->label);
596a9e9dd9cSNicolin Chen 
597a9e9dd9cSNicolin Chen 	/* Overwrite default shunt resistor value optionally */
598a6e43263SNicolin Chen 	if (!of_property_read_u32(child, "shunt-resistor-micro-ohms", &val)) {
599a6e43263SNicolin Chen 		if (val < 1 || val > INT_MAX) {
600a6e43263SNicolin Chen 			dev_err(dev, "invalid shunt resistor value %u of %s\n",
601a6e43263SNicolin Chen 				val, child->name);
602a6e43263SNicolin Chen 			return -EINVAL;
603a6e43263SNicolin Chen 		}
604a9e9dd9cSNicolin Chen 		input->shunt_resistor = val;
605a6e43263SNicolin Chen 	}
606a9e9dd9cSNicolin Chen 
607a9e9dd9cSNicolin Chen 	return 0;
608a9e9dd9cSNicolin Chen }
609a9e9dd9cSNicolin Chen 
610a9e9dd9cSNicolin Chen static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina)
611a9e9dd9cSNicolin Chen {
612a9e9dd9cSNicolin Chen 	const struct device_node *np = dev->of_node;
613a9e9dd9cSNicolin Chen 	struct device_node *child;
614a9e9dd9cSNicolin Chen 	int ret;
615a9e9dd9cSNicolin Chen 
616a9e9dd9cSNicolin Chen 	/* Compatible with non-DT platforms */
617a9e9dd9cSNicolin Chen 	if (!np)
618a9e9dd9cSNicolin Chen 		return 0;
619a9e9dd9cSNicolin Chen 
620a9e9dd9cSNicolin Chen 	for_each_child_of_node(np, child) {
621a9e9dd9cSNicolin Chen 		ret = ina3221_probe_child_from_dt(dev, child, ina);
622a9e9dd9cSNicolin Chen 		if (ret)
623a9e9dd9cSNicolin Chen 			return ret;
624a9e9dd9cSNicolin Chen 	}
625a9e9dd9cSNicolin Chen 
626a9e9dd9cSNicolin Chen 	return 0;
627a9e9dd9cSNicolin Chen }
628a9e9dd9cSNicolin Chen 
6297cb6dcffSAndrew F. Davis static int ina3221_probe(struct i2c_client *client,
6307cb6dcffSAndrew F. Davis 			 const struct i2c_device_id *id)
6317cb6dcffSAndrew F. Davis {
6327cb6dcffSAndrew F. Davis 	struct device *dev = &client->dev;
6337cb6dcffSAndrew F. Davis 	struct ina3221_data *ina;
6347cb6dcffSAndrew F. Davis 	struct device *hwmon_dev;
6357cb6dcffSAndrew F. Davis 	int i, ret;
6367cb6dcffSAndrew F. Davis 
6377cb6dcffSAndrew F. Davis 	ina = devm_kzalloc(dev, sizeof(*ina), GFP_KERNEL);
6387cb6dcffSAndrew F. Davis 	if (!ina)
6397cb6dcffSAndrew F. Davis 		return -ENOMEM;
6407cb6dcffSAndrew F. Davis 
6417cb6dcffSAndrew F. Davis 	ina->regmap = devm_regmap_init_i2c(client, &ina3221_regmap_config);
6427cb6dcffSAndrew F. Davis 	if (IS_ERR(ina->regmap)) {
6437cb6dcffSAndrew F. Davis 		dev_err(dev, "Unable to allocate register map\n");
6447cb6dcffSAndrew F. Davis 		return PTR_ERR(ina->regmap);
6457cb6dcffSAndrew F. Davis 	}
6467cb6dcffSAndrew F. Davis 
6477cb6dcffSAndrew F. Davis 	for (i = 0; i < F_MAX_FIELDS; i++) {
6487cb6dcffSAndrew F. Davis 		ina->fields[i] = devm_regmap_field_alloc(dev,
6497cb6dcffSAndrew F. Davis 							 ina->regmap,
6507cb6dcffSAndrew F. Davis 							 ina3221_reg_fields[i]);
6517cb6dcffSAndrew F. Davis 		if (IS_ERR(ina->fields[i])) {
6527cb6dcffSAndrew F. Davis 			dev_err(dev, "Unable to allocate regmap fields\n");
6537cb6dcffSAndrew F. Davis 			return PTR_ERR(ina->fields[i]);
6547cb6dcffSAndrew F. Davis 		}
6557cb6dcffSAndrew F. Davis 	}
6567cb6dcffSAndrew F. Davis 
6577cb6dcffSAndrew F. Davis 	for (i = 0; i < INA3221_NUM_CHANNELS; i++)
658a9e9dd9cSNicolin Chen 		ina->inputs[i].shunt_resistor = INA3221_RSHUNT_DEFAULT;
659a9e9dd9cSNicolin Chen 
660a9e9dd9cSNicolin Chen 	ret = ina3221_probe_from_dt(dev, ina);
661a9e9dd9cSNicolin Chen 	if (ret) {
662a9e9dd9cSNicolin Chen 		dev_err(dev, "Unable to probe from device tree\n");
663a9e9dd9cSNicolin Chen 		return ret;
664a9e9dd9cSNicolin Chen 	}
6657cb6dcffSAndrew F. Davis 
666323aeb0eSNicolin Chen 	/* The driver will be reset, so use reset value */
667323aeb0eSNicolin Chen 	ina->reg_config = INA3221_CONFIG_DEFAULT;
668a9e9dd9cSNicolin Chen 
669a9e9dd9cSNicolin Chen 	/* Disable channels if their inputs are disconnected */
670a9e9dd9cSNicolin Chen 	for (i = 0; i < INA3221_NUM_CHANNELS; i++) {
671a9e9dd9cSNicolin Chen 		if (ina->inputs[i].disconnected)
672a9e9dd9cSNicolin Chen 			ina->reg_config &= ~INA3221_CONFIG_CHx_EN(i);
673a9e9dd9cSNicolin Chen 	}
674a9e9dd9cSNicolin Chen 
675323aeb0eSNicolin Chen 	ina->pm_dev = dev;
67687625b24SNicolin Chen 	mutex_init(&ina->lock);
67759d608e1SNicolin Chen 	dev_set_drvdata(dev, ina);
67859d608e1SNicolin Chen 
679323aeb0eSNicolin Chen 	/* Enable PM runtime -- status is suspended by default */
680323aeb0eSNicolin Chen 	pm_runtime_enable(ina->pm_dev);
681323aeb0eSNicolin Chen 
682323aeb0eSNicolin Chen 	/* Initialize (resume) the device */
683323aeb0eSNicolin Chen 	for (i = 0; i < INA3221_NUM_CHANNELS; i++) {
684323aeb0eSNicolin Chen 		if (ina->inputs[i].disconnected)
685323aeb0eSNicolin Chen 			continue;
686323aeb0eSNicolin Chen 		/* Match the refcount with number of enabled channels */
687323aeb0eSNicolin Chen 		ret = pm_runtime_get_sync(ina->pm_dev);
688323aeb0eSNicolin Chen 		if (ret < 0)
689323aeb0eSNicolin Chen 			goto fail;
690323aeb0eSNicolin Chen 	}
691323aeb0eSNicolin Chen 
692d4b0166dSNicolin Chen 	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, ina,
693d4b0166dSNicolin Chen 							 &ina3221_chip_info,
694d4b0166dSNicolin Chen 							 ina3221_groups);
6957cb6dcffSAndrew F. Davis 	if (IS_ERR(hwmon_dev)) {
6967cb6dcffSAndrew F. Davis 		dev_err(dev, "Unable to register hwmon device\n");
697323aeb0eSNicolin Chen 		ret = PTR_ERR(hwmon_dev);
698323aeb0eSNicolin Chen 		goto fail;
6997cb6dcffSAndrew F. Davis 	}
7007cb6dcffSAndrew F. Davis 
7017cb6dcffSAndrew F. Davis 	return 0;
702323aeb0eSNicolin Chen 
703323aeb0eSNicolin Chen fail:
704323aeb0eSNicolin Chen 	pm_runtime_disable(ina->pm_dev);
705323aeb0eSNicolin Chen 	pm_runtime_set_suspended(ina->pm_dev);
706323aeb0eSNicolin Chen 	/* pm_runtime_put_noidle() will decrease the PM refcount until 0 */
707323aeb0eSNicolin Chen 	for (i = 0; i < INA3221_NUM_CHANNELS; i++)
708323aeb0eSNicolin Chen 		pm_runtime_put_noidle(ina->pm_dev);
709323aeb0eSNicolin Chen 	mutex_destroy(&ina->lock);
710323aeb0eSNicolin Chen 
711323aeb0eSNicolin Chen 	return ret;
7127cb6dcffSAndrew F. Davis }
7137cb6dcffSAndrew F. Davis 
71487625b24SNicolin Chen static int ina3221_remove(struct i2c_client *client)
71587625b24SNicolin Chen {
71687625b24SNicolin Chen 	struct ina3221_data *ina = dev_get_drvdata(&client->dev);
717323aeb0eSNicolin Chen 	int i;
718323aeb0eSNicolin Chen 
719323aeb0eSNicolin Chen 	pm_runtime_disable(ina->pm_dev);
720323aeb0eSNicolin Chen 	pm_runtime_set_suspended(ina->pm_dev);
721323aeb0eSNicolin Chen 
722323aeb0eSNicolin Chen 	/* pm_runtime_put_noidle() will decrease the PM refcount until 0 */
723323aeb0eSNicolin Chen 	for (i = 0; i < INA3221_NUM_CHANNELS; i++)
724323aeb0eSNicolin Chen 		pm_runtime_put_noidle(ina->pm_dev);
72587625b24SNicolin Chen 
72687625b24SNicolin Chen 	mutex_destroy(&ina->lock);
72787625b24SNicolin Chen 
72887625b24SNicolin Chen 	return 0;
72987625b24SNicolin Chen }
73087625b24SNicolin Chen 
731ead21c77SArnd Bergmann static int __maybe_unused ina3221_suspend(struct device *dev)
73259d608e1SNicolin Chen {
73359d608e1SNicolin Chen 	struct ina3221_data *ina = dev_get_drvdata(dev);
73459d608e1SNicolin Chen 	int ret;
73559d608e1SNicolin Chen 
73659d608e1SNicolin Chen 	/* Save config register value and enable cache-only */
73759d608e1SNicolin Chen 	ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config);
73859d608e1SNicolin Chen 	if (ret)
73959d608e1SNicolin Chen 		return ret;
74059d608e1SNicolin Chen 
74159d608e1SNicolin Chen 	/* Set to power-down mode for power saving */
74259d608e1SNicolin Chen 	ret = regmap_update_bits(ina->regmap, INA3221_CONFIG,
74359d608e1SNicolin Chen 				 INA3221_CONFIG_MODE_MASK,
74459d608e1SNicolin Chen 				 INA3221_CONFIG_MODE_POWERDOWN);
74559d608e1SNicolin Chen 	if (ret)
74659d608e1SNicolin Chen 		return ret;
74759d608e1SNicolin Chen 
74859d608e1SNicolin Chen 	regcache_cache_only(ina->regmap, true);
74959d608e1SNicolin Chen 	regcache_mark_dirty(ina->regmap);
75059d608e1SNicolin Chen 
75159d608e1SNicolin Chen 	return 0;
75259d608e1SNicolin Chen }
75359d608e1SNicolin Chen 
754ead21c77SArnd Bergmann static int __maybe_unused ina3221_resume(struct device *dev)
75559d608e1SNicolin Chen {
75659d608e1SNicolin Chen 	struct ina3221_data *ina = dev_get_drvdata(dev);
75759d608e1SNicolin Chen 	int ret;
75859d608e1SNicolin Chen 
75959d608e1SNicolin Chen 	regcache_cache_only(ina->regmap, false);
76059d608e1SNicolin Chen 
76159d608e1SNicolin Chen 	/* Software reset the chip */
76259d608e1SNicolin Chen 	ret = regmap_field_write(ina->fields[F_RST], true);
76359d608e1SNicolin Chen 	if (ret) {
76459d608e1SNicolin Chen 		dev_err(dev, "Unable to reset device\n");
76559d608e1SNicolin Chen 		return ret;
76659d608e1SNicolin Chen 	}
76759d608e1SNicolin Chen 
76859d608e1SNicolin Chen 	/* Restore cached register values to hardware */
76959d608e1SNicolin Chen 	ret = regcache_sync(ina->regmap);
77059d608e1SNicolin Chen 	if (ret)
77159d608e1SNicolin Chen 		return ret;
77259d608e1SNicolin Chen 
77359d608e1SNicolin Chen 	/* Restore config register value to hardware */
77459d608e1SNicolin Chen 	ret = regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config);
77559d608e1SNicolin Chen 	if (ret)
77659d608e1SNicolin Chen 		return ret;
77759d608e1SNicolin Chen 
77859d608e1SNicolin Chen 	return 0;
77959d608e1SNicolin Chen }
78059d608e1SNicolin Chen 
78159d608e1SNicolin Chen static const struct dev_pm_ops ina3221_pm = {
782323aeb0eSNicolin Chen 	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
783323aeb0eSNicolin Chen 				pm_runtime_force_resume)
784323aeb0eSNicolin Chen 	SET_RUNTIME_PM_OPS(ina3221_suspend, ina3221_resume, NULL)
78559d608e1SNicolin Chen };
78659d608e1SNicolin Chen 
7877cb6dcffSAndrew F. Davis static const struct of_device_id ina3221_of_match_table[] = {
7887cb6dcffSAndrew F. Davis 	{ .compatible = "ti,ina3221", },
7897cb6dcffSAndrew F. Davis 	{ /* sentinel */ }
7907cb6dcffSAndrew F. Davis };
7917cb6dcffSAndrew F. Davis MODULE_DEVICE_TABLE(of, ina3221_of_match_table);
7927cb6dcffSAndrew F. Davis 
7937cb6dcffSAndrew F. Davis static const struct i2c_device_id ina3221_ids[] = {
7947cb6dcffSAndrew F. Davis 	{ "ina3221", 0 },
7957cb6dcffSAndrew F. Davis 	{ /* sentinel */ }
7967cb6dcffSAndrew F. Davis };
7977cb6dcffSAndrew F. Davis MODULE_DEVICE_TABLE(i2c, ina3221_ids);
7987cb6dcffSAndrew F. Davis 
7997cb6dcffSAndrew F. Davis static struct i2c_driver ina3221_i2c_driver = {
8007cb6dcffSAndrew F. Davis 	.probe = ina3221_probe,
80187625b24SNicolin Chen 	.remove = ina3221_remove,
8027cb6dcffSAndrew F. Davis 	.driver = {
8037cb6dcffSAndrew F. Davis 		.name = INA3221_DRIVER_NAME,
8047cb6dcffSAndrew F. Davis 		.of_match_table = ina3221_of_match_table,
80559d608e1SNicolin Chen 		.pm = &ina3221_pm,
8067cb6dcffSAndrew F. Davis 	},
8077cb6dcffSAndrew F. Davis 	.id_table = ina3221_ids,
8087cb6dcffSAndrew F. Davis };
8097cb6dcffSAndrew F. Davis module_i2c_driver(ina3221_i2c_driver);
8107cb6dcffSAndrew F. Davis 
8117cb6dcffSAndrew F. Davis MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
8127cb6dcffSAndrew F. Davis MODULE_DESCRIPTION("Texas Instruments INA3221 HWMon Driver");
8137cb6dcffSAndrew F. Davis MODULE_LICENSE("GPL v2");
814