Skip to content

Commit f5afdd1

Browse files
nsolanki22broonie
authored andcommitted
regulator (max5970): Add hwmon support
Utilize the integrated 10-bit ADC in Max5970/Max5978 to enable voltage and current monitoring. This feature is seamlessly integrated through the hwmon subsystem. Signed-off-by: Naresh Solanki <[email protected]> Acked-by: Guenter Roeck <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent ae61939 commit f5afdd1

File tree

1 file changed

+143
-1
lines changed

1 file changed

+143
-1
lines changed

drivers/regulator/max5970-regulator.c

Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <linux/bitops.h>
1111
#include <linux/device.h>
1212
#include <linux/err.h>
13+
#include <linux/hwmon.h>
1314
#include <linux/module.h>
1415
#include <linux/io.h>
1516
#include <linux/of.h>
@@ -32,6 +33,132 @@ enum max597x_regulator_id {
3233
MAX597X_SW1,
3334
};
3435

36+
static int max5970_read_adc(struct regmap *regmap, int reg, long *val)
37+
{
38+
u8 reg_data[2];
39+
int ret;
40+
41+
ret = regmap_bulk_read(regmap, reg, &reg_data[0], 2);
42+
if (ret < 0)
43+
return ret;
44+
45+
*val = (reg_data[0] << 2) | (reg_data[1] & 3);
46+
47+
return 0;
48+
}
49+
50+
static int max5970_read(struct device *dev, enum hwmon_sensor_types type,
51+
u32 attr, int channel, long *val)
52+
{
53+
struct regulator_dev **rdevs = dev_get_drvdata(dev);
54+
struct max5970_regulator *ddata = rdev_get_drvdata(rdevs[channel]);
55+
struct regmap *regmap = ddata->regmap;
56+
int ret;
57+
58+
switch (type) {
59+
case hwmon_curr:
60+
switch (attr) {
61+
case hwmon_curr_input:
62+
ret = max5970_read_adc(regmap, MAX5970_REG_CURRENT_H(channel), val);
63+
if (ret < 0)
64+
return ret;
65+
/*
66+
* Calculate current from ADC value, IRNG range & shunt resistor value.
67+
* ddata->irng holds the voltage corresponding to the maximum value the
68+
* 10-bit ADC can measure.
69+
* To obtain the output, multiply the ADC value by the IRNG range (in
70+
* millivolts) and then divide it by the maximum value of the 10-bit ADC.
71+
*/
72+
*val = (*val * ddata->irng) >> 10;
73+
/* Convert the voltage meansurement across shunt resistor to current */
74+
*val = (*val * 1000) / ddata->shunt_micro_ohms;
75+
return 0;
76+
default:
77+
return -EOPNOTSUPP;
78+
}
79+
80+
case hwmon_in:
81+
switch (attr) {
82+
case hwmon_in_input:
83+
ret = max5970_read_adc(regmap, MAX5970_REG_VOLTAGE_H(channel), val);
84+
if (ret < 0)
85+
return ret;
86+
/*
87+
* Calculate voltage from ADC value and MON range.
88+
* ddata->mon_rng holds the voltage corresponding to the maximum value the
89+
* 10-bit ADC can measure.
90+
* To obtain the output, multiply the ADC value by the MON range (in
91+
* microvolts) and then divide it by the maximum value of the 10-bit ADC.
92+
*/
93+
*val = mul_u64_u32_shr(*val, ddata->mon_rng, 10);
94+
/* uV to mV */
95+
*val = *val / 1000;
96+
return 0;
97+
default:
98+
return -EOPNOTSUPP;
99+
}
100+
default:
101+
return -EOPNOTSUPP;
102+
}
103+
}
104+
105+
static umode_t max5970_is_visible(const void *data,
106+
enum hwmon_sensor_types type,
107+
u32 attr, int channel)
108+
{
109+
struct regulator_dev **rdevs = (struct regulator_dev **)data;
110+
struct max5970_regulator *ddata;
111+
112+
if (channel >= MAX5970_NUM_SWITCHES || !rdevs[channel])
113+
return 0;
114+
115+
ddata = rdev_get_drvdata(rdevs[channel]);
116+
117+
if (channel >= ddata->num_switches)
118+
return 0;
119+
120+
switch (type) {
121+
case hwmon_in:
122+
switch (attr) {
123+
case hwmon_in_input:
124+
return 0444;
125+
default:
126+
break;
127+
}
128+
break;
129+
case hwmon_curr:
130+
switch (attr) {
131+
case hwmon_curr_input:
132+
/* Current measurement requires knowledge of the shunt resistor value. */
133+
if (ddata->shunt_micro_ohms)
134+
return 0444;
135+
break;
136+
default:
137+
break;
138+
}
139+
break;
140+
default:
141+
break;
142+
}
143+
return 0;
144+
}
145+
146+
static const struct hwmon_ops max5970_hwmon_ops = {
147+
.is_visible = max5970_is_visible,
148+
.read = max5970_read,
149+
};
150+
151+
static const struct hwmon_channel_info *max5970_info[] = {
152+
HWMON_CHANNEL_INFO(in, HWMON_I_INPUT, HWMON_I_INPUT),
153+
HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT, HWMON_C_INPUT),
154+
NULL
155+
};
156+
157+
static const struct hwmon_chip_info max5970_chip_info = {
158+
.ops = &max5970_hwmon_ops,
159+
.info = max5970_info,
160+
};
161+
35162
static int max597x_uvp_ovp_check_mode(struct regulator_dev *rdev, int severity)
36163
{
37164
int ret, reg;
@@ -431,7 +558,8 @@ static int max597x_regulator_probe(struct platform_device *pdev)
431558
struct i2c_client *i2c = to_i2c_client(pdev->dev.parent);
432559
struct regulator_config config = { };
433560
struct regulator_dev *rdev;
434-
struct regulator_dev *rdevs[MAX5970_NUM_SWITCHES];
561+
struct regulator_dev **rdevs = NULL;
562+
struct device *hwmon_dev;
435563
int num_switches;
436564
int ret, i;
437565

@@ -442,6 +570,11 @@ static int max597x_regulator_probe(struct platform_device *pdev)
442570
if (!max597x)
443571
return -ENOMEM;
444572

573+
rdevs = devm_kcalloc(&i2c->dev, MAX5970_NUM_SWITCHES, sizeof(struct regulator_dev *),
574+
GFP_KERNEL);
575+
if (!rdevs)
576+
return -ENOMEM;
577+
445578
i2c_set_clientdata(i2c, max597x);
446579

447580
if (of_device_is_compatible(i2c->dev.of_node, "maxim,max5978"))
@@ -485,6 +618,15 @@ static int max597x_regulator_probe(struct platform_device *pdev)
485618
max597x->shunt_micro_ohms[i] = data->shunt_micro_ohms;
486619
}
487620

621+
if (IS_REACHABLE(CONFIG_HWMON)) {
622+
hwmon_dev = devm_hwmon_device_register_with_info(&i2c->dev, "max5970", rdevs,
623+
&max5970_chip_info, NULL);
624+
if (IS_ERR(hwmon_dev)) {
625+
return dev_err_probe(&i2c->dev, PTR_ERR(hwmon_dev),
626+
"Unable to register hwmon device\n");
627+
}
628+
}
629+
488630
if (i2c->irq) {
489631
ret =
490632
max597x_setup_irq(&i2c->dev, i2c->irq, rdevs, num_switches,

0 commit comments

Comments
 (0)