mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-15 08:44:14 +08:00
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 <xqiu@google.com> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
parent
b58bd4c6df
commit
5a56a39be7
@ -99,6 +99,25 @@ Sysfs entries for ina226, ina230 and ina231 only
|
|||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
|
|
||||||
======================= ====================================================
|
======================= ====================================================
|
||||||
|
in0_lcrit Critical low shunt voltage
|
||||||
|
in0_crit Critical high shunt voltage
|
||||||
|
in0_lcrit_alarm Shunt voltage critical low alarm
|
||||||
|
in0_crit_alarm Shunt voltage critical high alarm
|
||||||
|
in1_lcrit Critical low bus voltage
|
||||||
|
in1_crit Critical high bus voltage
|
||||||
|
in1_lcrit_alarm Bus voltage critical low alarm
|
||||||
|
in1_crit_alarm Bus voltage critical high alarm
|
||||||
|
power1_crit Critical high power
|
||||||
|
power1_crit_alarm Power critical high alarm
|
||||||
update_interval data conversion time; affects number of samples used
|
update_interval data conversion time; affects number of samples used
|
||||||
to average results for shunt and bus voltages.
|
to average results for shunt and bus voltages.
|
||||||
======================= ====================================================
|
======================= ====================================================
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
- Configure `shunt_resistor` before configure `power1_crit`, because power
|
||||||
|
value is calculated based on `shunt_resistor` set.
|
||||||
|
- Because of the underlying register implementation, only one `*crit` setting
|
||||||
|
and its `alarm` can be active. Writing to one `*crit` setting clears other
|
||||||
|
`*crit` settings and alarms. Writing 0 to any `*crit` setting clears all
|
||||||
|
`*crit` settings and alarms.
|
||||||
|
@ -74,6 +74,17 @@
|
|||||||
#define INA226_READ_AVG(reg) (((reg) & INA226_AVG_RD_MASK) >> 9)
|
#define INA226_READ_AVG(reg) (((reg) & INA226_AVG_RD_MASK) >> 9)
|
||||||
#define INA226_SHIFT_AVG(val) ((val) << 9)
|
#define INA226_SHIFT_AVG(val) ((val) << 9)
|
||||||
|
|
||||||
|
/* bit number of alert functions in Mask/Enable Register */
|
||||||
|
#define INA226_SHUNT_OVER_VOLTAGE_BIT 15
|
||||||
|
#define INA226_SHUNT_UNDER_VOLTAGE_BIT 14
|
||||||
|
#define INA226_BUS_OVER_VOLTAGE_BIT 13
|
||||||
|
#define INA226_BUS_UNDER_VOLTAGE_BIT 12
|
||||||
|
#define INA226_POWER_OVER_LIMIT_BIT 11
|
||||||
|
|
||||||
|
/* bit mask for alert config bits of Mask/Enable Register */
|
||||||
|
#define INA226_ALERT_CONFIG_MASK 0xFC00
|
||||||
|
#define INA226_ALERT_FUNCTION_FLAG BIT(4)
|
||||||
|
|
||||||
/* common attrs, ina226 attrs and NULL */
|
/* common attrs, ina226 attrs and NULL */
|
||||||
#define INA2XX_MAX_ATTRIBUTE_GROUPS 3
|
#define INA2XX_MAX_ATTRIBUTE_GROUPS 3
|
||||||
|
|
||||||
@ -303,6 +314,145 @@ static ssize_t ina2xx_value_show(struct device *dev,
|
|||||||
ina2xx_get_value(data, attr->index, regval));
|
ina2xx_get_value(data, attr->index, regval));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ina226_reg_to_alert(struct ina2xx_data *data, u8 bit, u16 regval)
|
||||||
|
{
|
||||||
|
int reg;
|
||||||
|
|
||||||
|
switch (bit) {
|
||||||
|
case INA226_SHUNT_OVER_VOLTAGE_BIT:
|
||||||
|
case INA226_SHUNT_UNDER_VOLTAGE_BIT:
|
||||||
|
reg = INA2XX_SHUNT_VOLTAGE;
|
||||||
|
break;
|
||||||
|
case INA226_BUS_OVER_VOLTAGE_BIT:
|
||||||
|
case INA226_BUS_UNDER_VOLTAGE_BIT:
|
||||||
|
reg = INA2XX_BUS_VOLTAGE;
|
||||||
|
break;
|
||||||
|
case INA226_POWER_OVER_LIMIT_BIT:
|
||||||
|
reg = INA2XX_POWER;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* programmer goofed */
|
||||||
|
WARN_ON_ONCE(1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ina2xx_get_value(data, reg, regval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Turns alert limit values into register values.
|
||||||
|
* Opposite of the formula in ina2xx_get_value().
|
||||||
|
*/
|
||||||
|
static s16 ina226_alert_to_reg(struct ina2xx_data *data, u8 bit, int val)
|
||||||
|
{
|
||||||
|
switch (bit) {
|
||||||
|
case INA226_SHUNT_OVER_VOLTAGE_BIT:
|
||||||
|
case INA226_SHUNT_UNDER_VOLTAGE_BIT:
|
||||||
|
val *= data->config->shunt_div;
|
||||||
|
return clamp_val(val, SHRT_MIN, SHRT_MAX);
|
||||||
|
case INA226_BUS_OVER_VOLTAGE_BIT:
|
||||||
|
case INA226_BUS_UNDER_VOLTAGE_BIT:
|
||||||
|
val = (val * 1000) << data->config->bus_voltage_shift;
|
||||||
|
val = DIV_ROUND_CLOSEST(val, data->config->bus_voltage_lsb);
|
||||||
|
return clamp_val(val, 0, SHRT_MAX);
|
||||||
|
case INA226_POWER_OVER_LIMIT_BIT:
|
||||||
|
val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW);
|
||||||
|
return clamp_val(val, 0, USHRT_MAX);
|
||||||
|
default:
|
||||||
|
/* programmer goofed */
|
||||||
|
WARN_ON_ONCE(1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ina226_alert_show(struct device *dev,
|
||||||
|
struct device_attribute *da, char *buf)
|
||||||
|
{
|
||||||
|
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||||
|
struct ina2xx_data *data = dev_get_drvdata(dev);
|
||||||
|
int regval;
|
||||||
|
int val = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&data->config_lock);
|
||||||
|
ret = regmap_read(data->regmap, INA226_MASK_ENABLE, ®val);
|
||||||
|
if (ret)
|
||||||
|
goto abort;
|
||||||
|
|
||||||
|
if (regval & BIT(attr->index)) {
|
||||||
|
ret = regmap_read(data->regmap, INA226_ALERT_LIMIT, ®val);
|
||||||
|
if (ret)
|
||||||
|
goto abort;
|
||||||
|
val = ina226_reg_to_alert(data, attr->index, regval);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||||
|
abort:
|
||||||
|
mutex_unlock(&data->config_lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ina226_alert_store(struct device *dev,
|
||||||
|
struct device_attribute *da,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||||
|
struct ina2xx_data *data = dev_get_drvdata(dev);
|
||||||
|
unsigned long val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = kstrtoul(buf, 10, &val);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear all alerts first to avoid accidentally triggering ALERT pin
|
||||||
|
* due to register write sequence. Then, only enable the alert
|
||||||
|
* if the value is non-zero.
|
||||||
|
*/
|
||||||
|
mutex_lock(&data->config_lock);
|
||||||
|
ret = regmap_update_bits(data->regmap, INA226_MASK_ENABLE,
|
||||||
|
INA226_ALERT_CONFIG_MASK, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
goto abort;
|
||||||
|
|
||||||
|
ret = regmap_write(data->regmap, INA226_ALERT_LIMIT,
|
||||||
|
ina226_alert_to_reg(data, attr->index, val));
|
||||||
|
if (ret < 0)
|
||||||
|
goto abort;
|
||||||
|
|
||||||
|
if (val != 0) {
|
||||||
|
ret = regmap_update_bits(data->regmap, INA226_MASK_ENABLE,
|
||||||
|
INA226_ALERT_CONFIG_MASK,
|
||||||
|
BIT(attr->index));
|
||||||
|
if (ret < 0)
|
||||||
|
goto abort;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = count;
|
||||||
|
abort:
|
||||||
|
mutex_unlock(&data->config_lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ina226_alarm_show(struct device *dev,
|
||||||
|
struct device_attribute *da, char *buf)
|
||||||
|
{
|
||||||
|
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||||
|
struct ina2xx_data *data = dev_get_drvdata(dev);
|
||||||
|
int regval;
|
||||||
|
int alarm = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = regmap_read(data->regmap, INA226_MASK_ENABLE, ®val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
alarm = (regval & BIT(attr->index)) &&
|
||||||
|
(regval & INA226_ALERT_FUNCTION_FLAG);
|
||||||
|
return snprintf(buf, PAGE_SIZE, "%d\n", alarm);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In order to keep calibration register value fixed, the product
|
* In order to keep calibration register value fixed, the product
|
||||||
* of current_lsb and shunt_resistor should also be fixed and equal
|
* 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,
|
|||||||
|
|
||||||
/* shunt voltage */
|
/* shunt voltage */
|
||||||
static SENSOR_DEVICE_ATTR_RO(in0_input, ina2xx_value, INA2XX_SHUNT_VOLTAGE);
|
static SENSOR_DEVICE_ATTR_RO(in0_input, ina2xx_value, INA2XX_SHUNT_VOLTAGE);
|
||||||
|
/* shunt voltage over/under voltage alert setting and alarm */
|
||||||
|
static SENSOR_DEVICE_ATTR_RW(in0_crit, ina226_alert,
|
||||||
|
INA226_SHUNT_OVER_VOLTAGE_BIT);
|
||||||
|
static SENSOR_DEVICE_ATTR_RW(in0_lcrit, ina226_alert,
|
||||||
|
INA226_SHUNT_UNDER_VOLTAGE_BIT);
|
||||||
|
static SENSOR_DEVICE_ATTR_RO(in0_crit_alarm, ina226_alarm,
|
||||||
|
INA226_SHUNT_OVER_VOLTAGE_BIT);
|
||||||
|
static SENSOR_DEVICE_ATTR_RO(in0_lcrit_alarm, ina226_alarm,
|
||||||
|
INA226_SHUNT_UNDER_VOLTAGE_BIT);
|
||||||
|
|
||||||
/* bus voltage */
|
/* bus voltage */
|
||||||
static SENSOR_DEVICE_ATTR_RO(in1_input, ina2xx_value, INA2XX_BUS_VOLTAGE);
|
static SENSOR_DEVICE_ATTR_RO(in1_input, ina2xx_value, INA2XX_BUS_VOLTAGE);
|
||||||
|
/* bus voltage over/under voltage alert setting and alarm */
|
||||||
|
static SENSOR_DEVICE_ATTR_RW(in1_crit, ina226_alert,
|
||||||
|
INA226_BUS_OVER_VOLTAGE_BIT);
|
||||||
|
static SENSOR_DEVICE_ATTR_RW(in1_lcrit, ina226_alert,
|
||||||
|
INA226_BUS_UNDER_VOLTAGE_BIT);
|
||||||
|
static SENSOR_DEVICE_ATTR_RO(in1_crit_alarm, ina226_alarm,
|
||||||
|
INA226_BUS_OVER_VOLTAGE_BIT);
|
||||||
|
static SENSOR_DEVICE_ATTR_RO(in1_lcrit_alarm, ina226_alarm,
|
||||||
|
INA226_BUS_UNDER_VOLTAGE_BIT);
|
||||||
|
|
||||||
/* calculated current */
|
/* calculated current */
|
||||||
static SENSOR_DEVICE_ATTR_RO(curr1_input, ina2xx_value, INA2XX_CURRENT);
|
static SENSOR_DEVICE_ATTR_RO(curr1_input, ina2xx_value, INA2XX_CURRENT);
|
||||||
|
|
||||||
/* calculated power */
|
/* calculated power */
|
||||||
static SENSOR_DEVICE_ATTR_RO(power1_input, ina2xx_value, INA2XX_POWER);
|
static SENSOR_DEVICE_ATTR_RO(power1_input, ina2xx_value, INA2XX_POWER);
|
||||||
|
/* over-limit power alert setting and alarm */
|
||||||
|
static SENSOR_DEVICE_ATTR_RW(power1_crit, ina226_alert,
|
||||||
|
INA226_POWER_OVER_LIMIT_BIT);
|
||||||
|
static SENSOR_DEVICE_ATTR_RO(power1_crit_alarm, ina226_alarm,
|
||||||
|
INA226_POWER_OVER_LIMIT_BIT);
|
||||||
|
|
||||||
/* shunt resistance */
|
/* shunt resistance */
|
||||||
static SENSOR_DEVICE_ATTR_RW(shunt_resistor, ina2xx_shunt, INA2XX_CALIBRATION);
|
static SENSOR_DEVICE_ATTR_RW(shunt_resistor, ina2xx_shunt, INA2XX_CALIBRATION);
|
||||||
@ -423,6 +596,16 @@ static const struct attribute_group ina2xx_group = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static struct attribute *ina226_attrs[] = {
|
static struct attribute *ina226_attrs[] = {
|
||||||
|
&sensor_dev_attr_in0_crit.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in0_lcrit.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in0_crit_alarm.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in0_lcrit_alarm.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in1_crit.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in1_lcrit.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in1_crit_alarm.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_in1_lcrit_alarm.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_power1_crit.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_power1_crit_alarm.dev_attr.attr,
|
||||||
&sensor_dev_attr_update_interval.dev_attr.attr,
|
&sensor_dev_attr_update_interval.dev_attr.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user