Skip to content

Commit 323aeb0

Browse files
Nicolin Chengroeck
authored andcommitted
hwmon: (ina3221) Add PM runtime support
If all three channels are disabled via in[123]_enable ABI, the driver could suspend the chip for power saving purpose. So this patch adds the PM runtime support in order to gain more power control than system suspend and resume use case. For PM runtime, there are a few related changes happening: 1) Added a new pm_dev device pointer for all the PM runtime callbacks. This is because hwmon core registers a child device for each hwmon driver and passes it back to each driver. So there might be a mismatch between two device pointers in the driver if mixing using them. 2) Added a check in ina3221_is_enabled() to make sure that the chip is resumed. 3) Bypassed the unchanged status in ina3221_write_enable() in order to keep the PM runtime refcount being matched. 4) Removed the reset routine in the probe() by calling the resume() via pm_runtime_get_sync() instead, as they're similar. It's also necessary to do so to match initial PM refcount with the number of enabled channels. Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
1 parent 4c0415a commit 323aeb0

File tree

1 file changed

+74
-19
lines changed

1 file changed

+74
-19
lines changed

drivers/hwmon/ina3221.c

Lines changed: 74 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <linux/module.h>
2121
#include <linux/mutex.h>
2222
#include <linux/of.h>
23+
#include <linux/pm_runtime.h>
2324
#include <linux/regmap.h>
2425

2526
#define INA3221_DRIVER_NAME "ina3221"
@@ -53,6 +54,7 @@
5354
#define INA3221_CONFIG_CHs_EN_MASK GENMASK(14, 12)
5455
#define INA3221_CONFIG_CHx_EN(x) BIT(14 - (x))
5556

57+
#define INA3221_CONFIG_DEFAULT 0x7127
5658
#define INA3221_RSHUNT_DEFAULT 10000
5759

5860
enum ina3221_fields {
@@ -103,13 +105,15 @@ struct ina3221_input {
103105

104106
/**
105107
* struct ina3221_data - device specific information
108+
* @pm_dev: Device pointer for pm runtime
106109
* @regmap: Register map of the device
107110
* @fields: Register fields of the device
108111
* @inputs: Array of channel input source specific structures
109112
* @lock: mutex lock to serialize sysfs attribute accesses
110113
* @reg_config: Register value of INA3221_CONFIG
111114
*/
112115
struct ina3221_data {
116+
struct device *pm_dev;
113117
struct regmap *regmap;
114118
struct regmap_field *fields[F_MAX_FIELDS];
115119
struct ina3221_input inputs[INA3221_NUM_CHANNELS];
@@ -119,7 +123,8 @@ struct ina3221_data {
119123

120124
static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel)
121125
{
122-
return ina->reg_config & INA3221_CONFIG_CHx_EN(channel);
126+
return pm_runtime_active(ina->pm_dev) &&
127+
(ina->reg_config & INA3221_CONFIG_CHx_EN(channel));
123128
}
124129

125130
/* Lookup table for Bus and Shunt conversion times in usec */
@@ -290,21 +295,48 @@ static int ina3221_write_enable(struct device *dev, int channel, bool enable)
290295
{
291296
struct ina3221_data *ina = dev_get_drvdata(dev);
292297
u16 config, mask = INA3221_CONFIG_CHx_EN(channel);
298+
u16 config_old = ina->reg_config & mask;
293299
int ret;
294300

295301
config = enable ? mask : 0;
296302

303+
/* Bypass if enable status is not being changed */
304+
if (config_old == config)
305+
return 0;
306+
307+
/* For enabling routine, increase refcount and resume() at first */
308+
if (enable) {
309+
ret = pm_runtime_get_sync(ina->pm_dev);
310+
if (ret < 0) {
311+
dev_err(dev, "Failed to get PM runtime\n");
312+
return ret;
313+
}
314+
}
315+
297316
/* Enable or disable the channel */
298317
ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, mask, config);
299318
if (ret)
300-
return ret;
319+
goto fail;
301320

302321
/* Cache the latest config register value */
303322
ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config);
304323
if (ret)
305-
return ret;
324+
goto fail;
325+
326+
/* For disabling routine, decrease refcount or suspend() at last */
327+
if (!enable)
328+
pm_runtime_put_sync(ina->pm_dev);
306329

307330
return 0;
331+
332+
fail:
333+
if (enable) {
334+
dev_err(dev, "Failed to enable channel %d: error %d\n",
335+
channel, ret);
336+
pm_runtime_put_sync(ina->pm_dev);
337+
}
338+
339+
return ret;
308340
}
309341

310342
static int ina3221_read(struct device *dev, enum hwmon_sensor_types type,
@@ -631,44 +663,65 @@ static int ina3221_probe(struct i2c_client *client,
631663
return ret;
632664
}
633665

634-
ret = regmap_field_write(ina->fields[F_RST], true);
635-
if (ret) {
636-
dev_err(dev, "Unable to reset device\n");
637-
return ret;
638-
}
639-
640-
/* Sync config register after reset */
641-
ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config);
642-
if (ret)
643-
return ret;
666+
/* The driver will be reset, so use reset value */
667+
ina->reg_config = INA3221_CONFIG_DEFAULT;
644668

645669
/* Disable channels if their inputs are disconnected */
646670
for (i = 0; i < INA3221_NUM_CHANNELS; i++) {
647671
if (ina->inputs[i].disconnected)
648672
ina->reg_config &= ~INA3221_CONFIG_CHx_EN(i);
649673
}
650-
ret = regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config);
651-
if (ret)
652-
return ret;
653674

675+
ina->pm_dev = dev;
654676
mutex_init(&ina->lock);
655677
dev_set_drvdata(dev, ina);
656678

679+
/* Enable PM runtime -- status is suspended by default */
680+
pm_runtime_enable(ina->pm_dev);
681+
682+
/* Initialize (resume) the device */
683+
for (i = 0; i < INA3221_NUM_CHANNELS; i++) {
684+
if (ina->inputs[i].disconnected)
685+
continue;
686+
/* Match the refcount with number of enabled channels */
687+
ret = pm_runtime_get_sync(ina->pm_dev);
688+
if (ret < 0)
689+
goto fail;
690+
}
691+
657692
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, ina,
658693
&ina3221_chip_info,
659694
ina3221_groups);
660695
if (IS_ERR(hwmon_dev)) {
661696
dev_err(dev, "Unable to register hwmon device\n");
662-
mutex_destroy(&ina->lock);
663-
return PTR_ERR(hwmon_dev);
697+
ret = PTR_ERR(hwmon_dev);
698+
goto fail;
664699
}
665700

666701
return 0;
702+
703+
fail:
704+
pm_runtime_disable(ina->pm_dev);
705+
pm_runtime_set_suspended(ina->pm_dev);
706+
/* pm_runtime_put_noidle() will decrease the PM refcount until 0 */
707+
for (i = 0; i < INA3221_NUM_CHANNELS; i++)
708+
pm_runtime_put_noidle(ina->pm_dev);
709+
mutex_destroy(&ina->lock);
710+
711+
return ret;
667712
}
668713

669714
static int ina3221_remove(struct i2c_client *client)
670715
{
671716
struct ina3221_data *ina = dev_get_drvdata(&client->dev);
717+
int i;
718+
719+
pm_runtime_disable(ina->pm_dev);
720+
pm_runtime_set_suspended(ina->pm_dev);
721+
722+
/* pm_runtime_put_noidle() will decrease the PM refcount until 0 */
723+
for (i = 0; i < INA3221_NUM_CHANNELS; i++)
724+
pm_runtime_put_noidle(ina->pm_dev);
672725

673726
mutex_destroy(&ina->lock);
674727

@@ -726,7 +779,9 @@ static int __maybe_unused ina3221_resume(struct device *dev)
726779
}
727780

728781
static const struct dev_pm_ops ina3221_pm = {
729-
SET_SYSTEM_SLEEP_PM_OPS(ina3221_suspend, ina3221_resume)
782+
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
783+
pm_runtime_force_resume)
784+
SET_RUNTIME_PM_OPS(ina3221_suspend, ina3221_resume, NULL)
730785
};
731786

732787
static const struct of_device_id ina3221_of_match_table[] = {

0 commit comments

Comments
 (0)