Skip to content

Commit 5a56a39

Browse files
alex310110groeck
authored andcommitted
hwmon: (ina2xx) Implement alert functions
Implement alert functions for INA226, INA230 and INA231. Expose 06h Mask/Enable and 07h Alert Limit registers via alert setting and alarm files. Signed-off-by: Alex Qiu <[email protected]> Signed-off-by: Guenter Roeck <[email protected]>
1 parent b58bd4c commit 5a56a39

File tree

2 files changed

+202
-0
lines changed

2 files changed

+202
-0
lines changed

Documentation/hwmon/ina2xx.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,25 @@ Sysfs entries for ina226, ina230 and ina231 only
9999
------------------------------------------------
100100

101101
======================= ====================================================
102+
in0_lcrit Critical low shunt voltage
103+
in0_crit Critical high shunt voltage
104+
in0_lcrit_alarm Shunt voltage critical low alarm
105+
in0_crit_alarm Shunt voltage critical high alarm
106+
in1_lcrit Critical low bus voltage
107+
in1_crit Critical high bus voltage
108+
in1_lcrit_alarm Bus voltage critical low alarm
109+
in1_crit_alarm Bus voltage critical high alarm
110+
power1_crit Critical high power
111+
power1_crit_alarm Power critical high alarm
102112
update_interval data conversion time; affects number of samples used
103113
to average results for shunt and bus voltages.
104114
======================= ====================================================
115+
116+
.. note::
117+
118+
- Configure `shunt_resistor` before configure `power1_crit`, because power
119+
value is calculated based on `shunt_resistor` set.
120+
- Because of the underlying register implementation, only one `*crit` setting
121+
and its `alarm` can be active. Writing to one `*crit` setting clears other
122+
`*crit` settings and alarms. Writing 0 to any `*crit` setting clears all
123+
`*crit` settings and alarms.

drivers/hwmon/ina2xx.c

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,17 @@
7474
#define INA226_READ_AVG(reg) (((reg) & INA226_AVG_RD_MASK) >> 9)
7575
#define INA226_SHIFT_AVG(val) ((val) << 9)
7676

77+
/* bit number of alert functions in Mask/Enable Register */
78+
#define INA226_SHUNT_OVER_VOLTAGE_BIT 15
79+
#define INA226_SHUNT_UNDER_VOLTAGE_BIT 14
80+
#define INA226_BUS_OVER_VOLTAGE_BIT 13
81+
#define INA226_BUS_UNDER_VOLTAGE_BIT 12
82+
#define INA226_POWER_OVER_LIMIT_BIT 11
83+
84+
/* bit mask for alert config bits of Mask/Enable Register */
85+
#define INA226_ALERT_CONFIG_MASK 0xFC00
86+
#define INA226_ALERT_FUNCTION_FLAG BIT(4)
87+
7788
/* common attrs, ina226 attrs and NULL */
7889
#define INA2XX_MAX_ATTRIBUTE_GROUPS 3
7990

@@ -303,6 +314,145 @@ static ssize_t ina2xx_value_show(struct device *dev,
303314
ina2xx_get_value(data, attr->index, regval));
304315
}
305316

317+
static int ina226_reg_to_alert(struct ina2xx_data *data, u8 bit, u16 regval)
318+
{
319+
int reg;
320+
321+
switch (bit) {
322+
case INA226_SHUNT_OVER_VOLTAGE_BIT:
323+
case INA226_SHUNT_UNDER_VOLTAGE_BIT:
324+
reg = INA2XX_SHUNT_VOLTAGE;
325+
break;
326+
case INA226_BUS_OVER_VOLTAGE_BIT:
327+
case INA226_BUS_UNDER_VOLTAGE_BIT:
328+
reg = INA2XX_BUS_VOLTAGE;
329+
break;
330+
case INA226_POWER_OVER_LIMIT_BIT:
331+
reg = INA2XX_POWER;
332+
break;
333+
default:
334+
/* programmer goofed */
335+
WARN_ON_ONCE(1);
336+
return 0;
337+
}
338+
339+
return ina2xx_get_value(data, reg, regval);
340+
}
341+
342+
/*
343+
* Turns alert limit values into register values.
344+
* Opposite of the formula in ina2xx_get_value().
345+
*/
346+
static s16 ina226_alert_to_reg(struct ina2xx_data *data, u8 bit, int val)
347+
{
348+
switch (bit) {
349+
case INA226_SHUNT_OVER_VOLTAGE_BIT:
350+
case INA226_SHUNT_UNDER_VOLTAGE_BIT:
351+
val *= data->config->shunt_div;
352+
return clamp_val(val, SHRT_MIN, SHRT_MAX);
353+
case INA226_BUS_OVER_VOLTAGE_BIT:
354+
case INA226_BUS_UNDER_VOLTAGE_BIT:
355+
val = (val * 1000) << data->config->bus_voltage_shift;
356+
val = DIV_ROUND_CLOSEST(val, data->config->bus_voltage_lsb);
357+
return clamp_val(val, 0, SHRT_MAX);
358+
case INA226_POWER_OVER_LIMIT_BIT:
359+
val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW);
360+
return clamp_val(val, 0, USHRT_MAX);
361+
default:
362+
/* programmer goofed */
363+
WARN_ON_ONCE(1);
364+
return 0;
365+
}
366+
}
367+
368+
static ssize_t ina226_alert_show(struct device *dev,
369+
struct device_attribute *da, char *buf)
370+
{
371+
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
372+
struct ina2xx_data *data = dev_get_drvdata(dev);
373+
int regval;
374+
int val = 0;
375+
int ret;
376+
377+
mutex_lock(&data->config_lock);
378+
ret = regmap_read(data->regmap, INA226_MASK_ENABLE, &regval);
379+
if (ret)
380+
goto abort;
381+
382+
if (regval & BIT(attr->index)) {
383+
ret = regmap_read(data->regmap, INA226_ALERT_LIMIT, &regval);
384+
if (ret)
385+
goto abort;
386+
val = ina226_reg_to_alert(data, attr->index, regval);
387+
}
388+
389+
ret = snprintf(buf, PAGE_SIZE, "%d\n", val);
390+
abort:
391+
mutex_unlock(&data->config_lock);
392+
return ret;
393+
}
394+
395+
static ssize_t ina226_alert_store(struct device *dev,
396+
struct device_attribute *da,
397+
const char *buf, size_t count)
398+
{
399+
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
400+
struct ina2xx_data *data = dev_get_drvdata(dev);
401+
unsigned long val;
402+
int ret;
403+
404+
ret = kstrtoul(buf, 10, &val);
405+
if (ret < 0)
406+
return ret;
407+
408+
/*
409+
* Clear all alerts first to avoid accidentally triggering ALERT pin
410+
* due to register write sequence. Then, only enable the alert
411+
* if the value is non-zero.
412+
*/
413+
mutex_lock(&data->config_lock);
414+
ret = regmap_update_bits(data->regmap, INA226_MASK_ENABLE,
415+
INA226_ALERT_CONFIG_MASK, 0);
416+
if (ret < 0)
417+
goto abort;
418+
419+
ret = regmap_write(data->regmap, INA226_ALERT_LIMIT,
420+
ina226_alert_to_reg(data, attr->index, val));
421+
if (ret < 0)
422+
goto abort;
423+
424+
if (val != 0) {
425+
ret = regmap_update_bits(data->regmap, INA226_MASK_ENABLE,
426+
INA226_ALERT_CONFIG_MASK,
427+
BIT(attr->index));
428+
if (ret < 0)
429+
goto abort;
430+
}
431+
432+
ret = count;
433+
abort:
434+
mutex_unlock(&data->config_lock);
435+
return ret;
436+
}
437+
438+
static ssize_t ina226_alarm_show(struct device *dev,
439+
struct device_attribute *da, char *buf)
440+
{
441+
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
442+
struct ina2xx_data *data = dev_get_drvdata(dev);
443+
int regval;
444+
int alarm = 0;
445+
int ret;
446+
447+
ret = regmap_read(data->regmap, INA226_MASK_ENABLE, &regval);
448+
if (ret)
449+
return ret;
450+
451+
alarm = (regval & BIT(attr->index)) &&
452+
(regval & INA226_ALERT_FUNCTION_FLAG);
453+
return snprintf(buf, PAGE_SIZE, "%d\n", alarm);
454+
}
455+
306456
/*
307457
* In order to keep calibration register value fixed, the product
308458
* of current_lsb and shunt_resistor should also be fixed and equal
@@ -392,15 +542,38 @@ static ssize_t ina226_interval_show(struct device *dev,
392542

393543
/* shunt voltage */
394544
static SENSOR_DEVICE_ATTR_RO(in0_input, ina2xx_value, INA2XX_SHUNT_VOLTAGE);
545+
/* shunt voltage over/under voltage alert setting and alarm */
546+
static SENSOR_DEVICE_ATTR_RW(in0_crit, ina226_alert,
547+
INA226_SHUNT_OVER_VOLTAGE_BIT);
548+
static SENSOR_DEVICE_ATTR_RW(in0_lcrit, ina226_alert,
549+
INA226_SHUNT_UNDER_VOLTAGE_BIT);
550+
static SENSOR_DEVICE_ATTR_RO(in0_crit_alarm, ina226_alarm,
551+
INA226_SHUNT_OVER_VOLTAGE_BIT);
552+
static SENSOR_DEVICE_ATTR_RO(in0_lcrit_alarm, ina226_alarm,
553+
INA226_SHUNT_UNDER_VOLTAGE_BIT);
395554

396555
/* bus voltage */
397556
static SENSOR_DEVICE_ATTR_RO(in1_input, ina2xx_value, INA2XX_BUS_VOLTAGE);
557+
/* bus voltage over/under voltage alert setting and alarm */
558+
static SENSOR_DEVICE_ATTR_RW(in1_crit, ina226_alert,
559+
INA226_BUS_OVER_VOLTAGE_BIT);
560+
static SENSOR_DEVICE_ATTR_RW(in1_lcrit, ina226_alert,
561+
INA226_BUS_UNDER_VOLTAGE_BIT);
562+
static SENSOR_DEVICE_ATTR_RO(in1_crit_alarm, ina226_alarm,
563+
INA226_BUS_OVER_VOLTAGE_BIT);
564+
static SENSOR_DEVICE_ATTR_RO(in1_lcrit_alarm, ina226_alarm,
565+
INA226_BUS_UNDER_VOLTAGE_BIT);
398566

399567
/* calculated current */
400568
static SENSOR_DEVICE_ATTR_RO(curr1_input, ina2xx_value, INA2XX_CURRENT);
401569

402570
/* calculated power */
403571
static SENSOR_DEVICE_ATTR_RO(power1_input, ina2xx_value, INA2XX_POWER);
572+
/* over-limit power alert setting and alarm */
573+
static SENSOR_DEVICE_ATTR_RW(power1_crit, ina226_alert,
574+
INA226_POWER_OVER_LIMIT_BIT);
575+
static SENSOR_DEVICE_ATTR_RO(power1_crit_alarm, ina226_alarm,
576+
INA226_POWER_OVER_LIMIT_BIT);
404577

405578
/* shunt resistance */
406579
static SENSOR_DEVICE_ATTR_RW(shunt_resistor, ina2xx_shunt, INA2XX_CALIBRATION);
@@ -423,6 +596,16 @@ static const struct attribute_group ina2xx_group = {
423596
};
424597

425598
static struct attribute *ina226_attrs[] = {
599+
&sensor_dev_attr_in0_crit.dev_attr.attr,
600+
&sensor_dev_attr_in0_lcrit.dev_attr.attr,
601+
&sensor_dev_attr_in0_crit_alarm.dev_attr.attr,
602+
&sensor_dev_attr_in0_lcrit_alarm.dev_attr.attr,
603+
&sensor_dev_attr_in1_crit.dev_attr.attr,
604+
&sensor_dev_attr_in1_lcrit.dev_attr.attr,
605+
&sensor_dev_attr_in1_crit_alarm.dev_attr.attr,
606+
&sensor_dev_attr_in1_lcrit_alarm.dev_attr.attr,
607+
&sensor_dev_attr_power1_crit.dev_attr.attr,
608+
&sensor_dev_attr_power1_crit_alarm.dev_attr.attr,
426609
&sensor_dev_attr_update_interval.dev_attr.attr,
427610
NULL,
428611
};

0 commit comments

Comments
 (0)