Skip to contentSkip to conversation

Add suspend and resume functions

PATCH Applied Nicolin Chen 2 participants 8 messages Sep 29, 2018 – Oct 1, 2018 Hardware monitoring

lore
Share & tools
lore
#0 lore raw

v2 cover Add suspend and resume functions

This series of patches add system suspend and resume functiosn for ina3221 hwmon driver. To do so, it also needs two small fixes.

Changelog
v1->v2:
* Added PATCH-2 to fix macros
* Added power-down setting during suspend in PATCH-3

Nicolin Chen (3):
hwmon: ina3221: Add INA3221_CONFIG to volatile_table
hwmon: ina3221: Fix INA3221_CONFIG_MODE macros
hwmon: ina3221: Add suspend and resume functions

 drivers/hwmon/ina3221.c | 71 ++++++++++++++++++++++++++++++++++++++---
 1 file changed, 67 insertions(+), 4 deletions(-)
Signature · 1 line
2.17.1
#1 lore raw

PATCH v2 1/3 hwmon: ina3221: Add INA3221_CONFIG to volatile_table

The MSB (15th bit) of INA3221_CONFIG is a self-clear reset bit. So this register should be added to the volatile_table of the regmap_config. Otherwise, we will see this bit is sticky in the regcache which might accidentally reset the chip when an actual write happens to the register.

This might not be a severe bug for the current code line since there's no second place touching the INA3221_CONFIG except the reset routine in the probe().

---
 drivers/hwmon/ina3221.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hwmon/ina3221.c +1 -1
index e6b49500c52a..cfe65ff01051 100644
@@ -353,7 +353,7 @@ static struct attribute *ina3221_attrs[] = {
 ATTRIBUTE_GROUPS(ina3221);
 
 static const struct regmap_range ina3221_yes_ranges[] = {
-	regmap_reg_range(INA3221_SHUNT1, INA3221_BUS3),
+	regmap_reg_range(INA3221_CONFIG, INA3221_BUS3),
 	regmap_reg_range(INA3221_MASK_ENABLE, INA3221_MASK_ENABLE),
 };
 
Signature · 1 line
2.17.1
maintainer Applied
#2 lore raw

Re: [PATCH v2 1/3] hwmon: ina3221: Add INA3221_CONFIG to volatile_table

Quoted 11 lines · Nicolin Chen

The MSB (15th bit) of INA3221_CONFIG is a self-clear reset bit. So this register should be added to the volatile_table of the regmap_config. Otherwise, we will see this bit is sticky in the regcache which might accidentally reset the chip when an actual write happens to the register.

This might not be a severe bug for the current code line since there's no second place touching the INA3221_CONFIG except the reset routine in the probe().

Applied to hwmon-next.

Thanks,
Guenter

Quoted 17 lines
---
 drivers/hwmon/ina3221.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hwmon/ina3221.c +1 -1
index e6b49500c52a..cfe65ff01051 100644
@@ -353,7 +353,7 @@ static struct attribute *ina3221_attrs[] = {
 ATTRIBUTE_GROUPS(ina3221);
 
 static const struct regmap_range ina3221_yes_ranges[] = {
-	regmap_reg_range(INA3221_SHUNT1, INA3221_BUS3),
+	regmap_reg_range(INA3221_CONFIG, INA3221_BUS3),
 	regmap_reg_range(INA3221_MASK_ENABLE, INA3221_MASK_ENABLE),
 };
 
#3 lore raw

PATCH v2 2/3 hwmon: ina3221: Fix INA3221_CONFIG_MODE macros

In reply to Nicolin Chen

The three INA3221_CONFIG_MODE macros are not correctly defined here. The MODE3-1 filed is loacted at BIT 2-0 according to the datasheet.

So this patch just fixes them by shifting all of them with a correct offset. However, this isn't a crital bug fix as the driver does not use any of them at this point.

---
 drivers/hwmon/ina3221.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/hwmon/ina3221.c +3 -3
index cfe65ff01051..e0c4f4d83f4e 100644
@@ -38,9 +38,9 @@
 #define INA3221_WARN3			0x0c
 #define INA3221_MASK_ENABLE		0x0f
 
-#define INA3221_CONFIG_MODE_SHUNT	BIT(1)
-#define INA3221_CONFIG_MODE_BUS		BIT(2)
-#define INA3221_CONFIG_MODE_CONTINUOUS	BIT(3)
+#define INA3221_CONFIG_MODE_SHUNT	BIT(0)
+#define INA3221_CONFIG_MODE_BUS		BIT(1)
+#define INA3221_CONFIG_MODE_CONTINUOUS	BIT(2)
 
 #define INA3221_RSHUNT_DEFAULT		10000
 
Signature · 1 line
2.17.1
maintainer
#4 lore raw

Re: [PATCH v2 2/3] hwmon: ina3221: Fix INA3221_CONFIG_MODE macros

Quoted 3 lines · Nicolin Chen

The three INA3221_CONFIG_MODE macros are not correctly defined here. The MODE3-1 filed is loacted at BIT 2-0 according to the datasheet.

located

Quoted 5 lines

So this patch just fixes them by shifting all of them with a correct offset. However, this isn't a crital bug fix as the driver does not use any of them at this point.

No need to resend; I fixed it up. Applied to hwmon-next.

Thanks,
Guenter

Quoted 21 lines
---
 drivers/hwmon/ina3221.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/hwmon/ina3221.c +3 -3
index cfe65ff01051..e0c4f4d83f4e 100644
@@ -38,9 +38,9 @@
 #define INA3221_WARN3			0x0c
 #define INA3221_MASK_ENABLE		0x0f
 
-#define INA3221_CONFIG_MODE_SHUNT	BIT(1)
-#define INA3221_CONFIG_MODE_BUS		BIT(2)
-#define INA3221_CONFIG_MODE_CONTINUOUS	BIT(3)
+#define INA3221_CONFIG_MODE_SHUNT	BIT(0)
+#define INA3221_CONFIG_MODE_BUS		BIT(1)
+#define INA3221_CONFIG_MODE_CONTINUOUS	BIT(2)
 
 #define INA3221_RSHUNT_DEFAULT		10000
 
#5 lore raw

PATCH v2 3/3 hwmon: ina3221: Add suspend and resume functions

In reply to Nicolin Chen

Depending on the hardware design, an INA3221 chip might lose its power during system suspend/resume. So this patch adds a set of suspend and resume functions to cache the register values including config register value and limit settings.

---
Changelog
v1->v2:
* Added power-down setting during suspend for power saving

 drivers/hwmon/ina3221.c | 63 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)
drivers/hwmon/ina3221.c +63 -0
index e0c4f4d83f4e..877952efaa88 100644
@@ -38,6 +38,8 @@
 #define INA3221_WARN3			0x0c
 #define INA3221_MASK_ENABLE		0x0f
 
+#define INA3221_CONFIG_MODE_MASK	GENMASK(2, 0)
+#define INA3221_CONFIG_MODE_POWERDOWN	0
 #define INA3221_CONFIG_MODE_SHUNT	BIT(0)
 #define INA3221_CONFIG_MODE_BUS		BIT(1)
 #define INA3221_CONFIG_MODE_CONTINUOUS	BIT(2)
@@ -91,11 +93,13 @@ static const unsigned int register_channel[] = {
  * @regmap: Register map of the device
  * @fields: Register fields of the device
  * @shunt_resistors: Array of resistor values per channel
+ * @reg_config: Register value of INA3221_CONFIG
  */
 struct ina3221_data {
 	struct regmap *regmap;
 	struct regmap_field *fields[F_MAX_FIELDS];
 	int shunt_resistors[INA3221_NUM_CHANNELS];
+	u32 reg_config;
 };
 
 static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg,
@@ -415,9 +419,67 @@ static int ina3221_probe(struct i2c_client *client,
 		return PTR_ERR(hwmon_dev);
 	}
 
+	dev_set_drvdata(dev, ina);
+
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int ina3221_suspend(struct device *dev)
+{
+	struct ina3221_data *ina = dev_get_drvdata(dev);
+	int ret;
+
+	/* Save config register value and enable cache-only */
+	ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config);
+	if (ret)
+		return ret;
+
+	/* Set to power-down mode for power saving */
+	ret = regmap_update_bits(ina->regmap, INA3221_CONFIG,
+				 INA3221_CONFIG_MODE_MASK,
+				 INA3221_CONFIG_MODE_POWERDOWN);
+	if (ret)
+		return ret;
+
+	regcache_cache_only(ina->regmap, true);
+	regcache_mark_dirty(ina->regmap);
+
+	return 0;
+}
+
+static int ina3221_resume(struct device *dev)
+{
+	struct ina3221_data *ina = dev_get_drvdata(dev);
+	int ret;
+
+	regcache_cache_only(ina->regmap, false);
+
+	/* Software reset the chip */
+	ret = regmap_field_write(ina->fields[F_RST], true);
+	if (ret) {
+		dev_err(dev, "Unable to reset device\n");
+		return ret;
+	}
+
+	/* Restore cached register values to hardware */
+	ret = regcache_sync(ina->regmap);
+	if (ret)
+		return ret;
+
+	/* Restore config register value to hardware */
+	ret = regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops ina3221_pm = {
+	SET_SYSTEM_SLEEP_PM_OPS(ina3221_suspend, ina3221_resume)
+};
+
 static const struct of_device_id ina3221_of_match_table[] = {
 	{ .compatible = "ti,ina3221", },
 	{ /* sentinel */ }
@@ -435,6 +497,7 @@ static struct i2c_driver ina3221_i2c_driver = {
 	.driver = {
 		.name = INA3221_DRIVER_NAME,
 		.of_match_table = ina3221_of_match_table,
+		.pm = &ina3221_pm,
 	},
 	.id_table = ina3221_ids,
 };
Signature · 1 line
2.17.1
maintainer Applied
#6 lore raw

Re: [PATCH v2 3/3] hwmon: ina3221: Add suspend and resume functions

Quoted 6 lines · Nicolin Chen

Depending on the hardware design, an INA3221 chip might lose its power during system suspend/resume. So this patch adds a set of suspend and resume functions to cache the register values including config register value and limit settings.

Applied to hwmon-next, with a minor change; see below.

Thanks,
Guenter

Quoted 36 lines

---
Changelog
v1->v2:
* Added power-down setting during suspend for power saving

 drivers/hwmon/ina3221.c | 63 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)
drivers/hwmon/ina3221.c +4 -0
index e0c4f4d83f4e..877952efaa88 100644
@@ -38,6 +38,8 @@
 #define INA3221_WARN3			0x0c
 #define INA3221_MASK_ENABLE		0x0f
 
+#define INA3221_CONFIG_MODE_MASK	GENMASK(2, 0)
+#define INA3221_CONFIG_MODE_POWERDOWN	0
 #define INA3221_CONFIG_MODE_SHUNT	BIT(0)
 #define INA3221_CONFIG_MODE_BUS		BIT(1)
 #define INA3221_CONFIG_MODE_CONTINUOUS	BIT(2)
@@ -91,11 +93,13 @@ static const unsigned int register_channel[] = {
  * @regmap: Register map of the device
  * @fields: Register fields of the device
  * @shunt_resistors: Array of resistor values per channel
+ * @reg_config: Register value of INA3221_CONFIG
  */
 struct ina3221_data {
 	struct regmap *regmap;
 	struct regmap_field *fields[F_MAX_FIELDS];
 	int shunt_resistors[INA3221_NUM_CHANNELS];
+	u32 reg_config;
 };
 
 static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg,

drivers/hwmon/ina3221.c static int ina3221_probe(struct i2c_client *client, · line 423

Quoted 5 lines
drivers/hwmon/ina3221.c +2 -0
@@ -415,9 +419,67 @@ static int ina3221_probe(struct i2c_client *client,
 		return PTR_ERR(hwmon_dev);
 	}
 
+	dev_set_drvdata(dev, ina);
+

It is better to call that function before registering the hwmon device, to avoid the theroretical possibility that suspend is called before drvdata is set. I moved the call accordingly.

Quoted 70 lines
drivers/hwmon/ina3221.c +57 -0
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int ina3221_suspend(struct device *dev)
+{
+	struct ina3221_data *ina = dev_get_drvdata(dev);
+	int ret;
+
+	/* Save config register value and enable cache-only */
+	ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config);
+	if (ret)
+		return ret;
+
+	/* Set to power-down mode for power saving */
+	ret = regmap_update_bits(ina->regmap, INA3221_CONFIG,
+				 INA3221_CONFIG_MODE_MASK,
+				 INA3221_CONFIG_MODE_POWERDOWN);
+	if (ret)
+		return ret;
+
+	regcache_cache_only(ina->regmap, true);
+	regcache_mark_dirty(ina->regmap);
+
+	return 0;
+}
+
+static int ina3221_resume(struct device *dev)
+{
+	struct ina3221_data *ina = dev_get_drvdata(dev);
+	int ret;
+
+	regcache_cache_only(ina->regmap, false);
+
+	/* Software reset the chip */
+	ret = regmap_field_write(ina->fields[F_RST], true);
+	if (ret) {
+		dev_err(dev, "Unable to reset device\n");
+		return ret;
+	}
+
+	/* Restore cached register values to hardware */
+	ret = regcache_sync(ina->regmap);
+	if (ret)
+		return ret;
+
+	/* Restore config register value to hardware */
+	ret = regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops ina3221_pm = {
+	SET_SYSTEM_SLEEP_PM_OPS(ina3221_suspend, ina3221_resume)
+};
+
 static const struct of_device_id ina3221_of_match_table[] = {
 	{ .compatible = "ti,ina3221", },
 	{ /* sentinel */ }
@@ -435,6 +497,7 @@ static struct i2c_driver ina3221_i2c_driver = {
 	.driver = {
 		.name = INA3221_DRIVER_NAME,
 		.of_match_table = ina3221_of_match_table,
+		.pm = &ina3221_pm,
 	},
 	.id_table = ina3221_ids,
 };
#7 lore raw

Re: [PATCH v2 3/3] hwmon: ina3221: Add suspend and resume functions

Quoted 11 lines · Guenter Roeck

drivers/hwmon/ina3221.c static int ina3221_probe(struct i2c_client *client, · line 423

Quoted 7 lines

static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg,

drivers/hwmon/ina3221.c +2 -0
@@ -415,9 +419,67 @@ static int ina3221_probe(struct i2c_client *client,
 		return PTR_ERR(hwmon_dev);
 	}
 
+	dev_set_drvdata(dev, ina);
+

It is better to call that function before registering the hwmon device, to avoid the theroretical possibility that suspend is called before drvdata is set. I moved the call accordingly.

Oh..I see. Another lesson learned. Thank you for the fix!