mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-02 12:38:01 +00:00
hwmon: (pmbus/zl6100) Add support for VMON/VDRV
Some of the ZL6100 compatible chips support monitoring a separate voltage pin, VMON (ZL2004) or VDRV (ZL91xx). Report it as in2 / vmon. The chips support implicit warning limits for VMON/VDRV, as percentage of the respective critical voltage. Support by reading/writing the critical voltages and calculating the associated warning voltages. Signed-off-by: Guenter Roeck <linux@roeck-us.net>
This commit is contained in:
parent
ce603b18f3
commit
1640eaecc4
2 changed files with 188 additions and 14 deletions
|
@ -121,12 +121,26 @@ in1_max_alarm Input voltage high alarm.
|
||||||
in1_lcrit_alarm Input voltage critical low alarm.
|
in1_lcrit_alarm Input voltage critical low alarm.
|
||||||
in1_crit_alarm Input voltage critical high alarm.
|
in1_crit_alarm Input voltage critical high alarm.
|
||||||
|
|
||||||
in2_label "vout1"
|
in2_label "vmon"
|
||||||
in2_input Measured output voltage.
|
in2_input Measured voltage on VMON (ZL2004) or VDRV (ZL9101M,
|
||||||
in2_lcrit Critical minimum output Voltage.
|
ZL9117M) pin. Reported voltage is 16x the voltage on the
|
||||||
in2_crit Critical maximum output voltage.
|
pin (adjusted internally by the chip).
|
||||||
in2_lcrit_alarm Critical output voltage critical low alarm.
|
in2_lcrit Critical minumum VMON/VDRV Voltage.
|
||||||
in2_crit_alarm Critical output voltage critical high alarm.
|
in2_crit Critical maximum VMON/VDRV voltage.
|
||||||
|
in2_lcrit_alarm VMON/VDRV voltage critical low alarm.
|
||||||
|
in2_crit_alarm VMON/VDRV voltage critical high alarm.
|
||||||
|
|
||||||
|
vmon attributes are supported on ZL2004, ZL9101M,
|
||||||
|
and ZL9117M only.
|
||||||
|
|
||||||
|
inX_label "vout1"
|
||||||
|
inX_input Measured output voltage.
|
||||||
|
inX_lcrit Critical minimum output Voltage.
|
||||||
|
inX_crit Critical maximum output voltage.
|
||||||
|
inX_lcrit_alarm Critical output voltage critical low alarm.
|
||||||
|
inX_crit_alarm Critical output voltage critical high alarm.
|
||||||
|
|
||||||
|
X is 3 for ZL2004, ZL9101M, and ZL9117M, 2 otherwise.
|
||||||
|
|
||||||
curr1_label "iout1"
|
curr1_label "iout1"
|
||||||
curr1_input Measured output current.
|
curr1_input Measured output current.
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* Hardware monitoring driver for ZL6100 and compatibles
|
* Hardware monitoring driver for ZL6100 and compatibles
|
||||||
*
|
*
|
||||||
* Copyright (c) 2011 Ericsson AB.
|
* Copyright (c) 2011 Ericsson AB.
|
||||||
|
* Copyright (c) 2012 Guenter Roeck
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -45,12 +46,87 @@ struct zl6100_data {
|
||||||
|
|
||||||
#define ZL6100_MFR_XTEMP_ENABLE (1 << 7)
|
#define ZL6100_MFR_XTEMP_ENABLE (1 << 7)
|
||||||
|
|
||||||
|
#define MFR_VMON_OV_FAULT_LIMIT 0xf5
|
||||||
|
#define MFR_VMON_UV_FAULT_LIMIT 0xf6
|
||||||
|
#define MFR_READ_VMON 0xf7
|
||||||
|
|
||||||
|
#define VMON_UV_WARNING (1 << 5)
|
||||||
|
#define VMON_OV_WARNING (1 << 4)
|
||||||
|
#define VMON_UV_FAULT (1 << 1)
|
||||||
|
#define VMON_OV_FAULT (1 << 0)
|
||||||
|
|
||||||
#define ZL6100_WAIT_TIME 1000 /* uS */
|
#define ZL6100_WAIT_TIME 1000 /* uS */
|
||||||
|
|
||||||
static ushort delay = ZL6100_WAIT_TIME;
|
static ushort delay = ZL6100_WAIT_TIME;
|
||||||
module_param(delay, ushort, 0644);
|
module_param(delay, ushort, 0644);
|
||||||
MODULE_PARM_DESC(delay, "Delay between chip accesses in uS");
|
MODULE_PARM_DESC(delay, "Delay between chip accesses in uS");
|
||||||
|
|
||||||
|
/* Convert linear sensor value to milli-units */
|
||||||
|
static long zl6100_l2d(s16 l)
|
||||||
|
{
|
||||||
|
s16 exponent;
|
||||||
|
s32 mantissa;
|
||||||
|
long val;
|
||||||
|
|
||||||
|
exponent = l >> 11;
|
||||||
|
mantissa = ((s16)((l & 0x7ff) << 5)) >> 5;
|
||||||
|
|
||||||
|
val = mantissa;
|
||||||
|
|
||||||
|
/* scale result to milli-units */
|
||||||
|
val = val * 1000L;
|
||||||
|
|
||||||
|
if (exponent >= 0)
|
||||||
|
val <<= exponent;
|
||||||
|
else
|
||||||
|
val >>= -exponent;
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_MANTISSA (1023 * 1000)
|
||||||
|
#define MIN_MANTISSA (511 * 1000)
|
||||||
|
|
||||||
|
static u16 zl6100_d2l(long val)
|
||||||
|
{
|
||||||
|
s16 exponent = 0, mantissa;
|
||||||
|
bool negative = false;
|
||||||
|
|
||||||
|
/* simple case */
|
||||||
|
if (val == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (val < 0) {
|
||||||
|
negative = true;
|
||||||
|
val = -val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reduce large mantissa until it fits into 10 bit */
|
||||||
|
while (val >= MAX_MANTISSA && exponent < 15) {
|
||||||
|
exponent++;
|
||||||
|
val >>= 1;
|
||||||
|
}
|
||||||
|
/* Increase small mantissa to improve precision */
|
||||||
|
while (val < MIN_MANTISSA && exponent > -15) {
|
||||||
|
exponent--;
|
||||||
|
val <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert mantissa from milli-units to units */
|
||||||
|
mantissa = DIV_ROUND_CLOSEST(val, 1000);
|
||||||
|
|
||||||
|
/* Ensure that resulting number is within range */
|
||||||
|
if (mantissa > 0x3ff)
|
||||||
|
mantissa = 0x3ff;
|
||||||
|
|
||||||
|
/* restore sign */
|
||||||
|
if (negative)
|
||||||
|
mantissa = -mantissa;
|
||||||
|
|
||||||
|
/* Convert to 5 bit exponent, 11 bit mantissa */
|
||||||
|
return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800);
|
||||||
|
}
|
||||||
|
|
||||||
/* Some chips need a delay between accesses */
|
/* Some chips need a delay between accesses */
|
||||||
static inline void zl6100_wait(const struct zl6100_data *data)
|
static inline void zl6100_wait(const struct zl6100_data *data)
|
||||||
{
|
{
|
||||||
|
@ -65,9 +141,9 @@ static int zl6100_read_word_data(struct i2c_client *client, int page, int reg)
|
||||||
{
|
{
|
||||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||||
struct zl6100_data *data = to_zl6100_data(info);
|
struct zl6100_data *data = to_zl6100_data(info);
|
||||||
int ret;
|
int ret, vreg;
|
||||||
|
|
||||||
if (page || reg >= PMBUS_VIRT_BASE)
|
if (page > 0)
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
if (data->id == zl2005) {
|
if (data->id == zl2005) {
|
||||||
|
@ -83,9 +159,39 @@ static int zl6100_read_word_data(struct i2c_client *client, int page, int reg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case PMBUS_VIRT_READ_VMON:
|
||||||
|
vreg = MFR_READ_VMON;
|
||||||
|
break;
|
||||||
|
case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
|
||||||
|
case PMBUS_VIRT_VMON_OV_FAULT_LIMIT:
|
||||||
|
vreg = MFR_VMON_OV_FAULT_LIMIT;
|
||||||
|
break;
|
||||||
|
case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
|
||||||
|
case PMBUS_VIRT_VMON_UV_FAULT_LIMIT:
|
||||||
|
vreg = MFR_VMON_UV_FAULT_LIMIT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (reg >= PMBUS_VIRT_BASE)
|
||||||
|
return -ENXIO;
|
||||||
|
vreg = reg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
zl6100_wait(data);
|
zl6100_wait(data);
|
||||||
ret = pmbus_read_word_data(client, page, reg);
|
ret = pmbus_read_word_data(client, page, vreg);
|
||||||
data->access = ktime_get();
|
data->access = ktime_get();
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
|
||||||
|
ret = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(ret) * 9, 10));
|
||||||
|
break;
|
||||||
|
case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
|
||||||
|
ret = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(ret) * 11, 10));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -94,13 +200,35 @@ static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg)
|
||||||
{
|
{
|
||||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||||
struct zl6100_data *data = to_zl6100_data(info);
|
struct zl6100_data *data = to_zl6100_data(info);
|
||||||
int ret;
|
int ret, status;
|
||||||
|
|
||||||
if (page > 0)
|
if (page > 0)
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
zl6100_wait(data);
|
zl6100_wait(data);
|
||||||
ret = pmbus_read_byte_data(client, page, reg);
|
|
||||||
|
switch (reg) {
|
||||||
|
case PMBUS_VIRT_STATUS_VMON:
|
||||||
|
ret = pmbus_read_byte_data(client, 0,
|
||||||
|
PMBUS_STATUS_MFR_SPECIFIC);
|
||||||
|
if (ret < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
status = 0;
|
||||||
|
if (ret & VMON_UV_WARNING)
|
||||||
|
status |= PB_VOLTAGE_UV_WARNING;
|
||||||
|
if (ret & VMON_OV_WARNING)
|
||||||
|
status |= PB_VOLTAGE_OV_WARNING;
|
||||||
|
if (ret & VMON_UV_FAULT)
|
||||||
|
status |= PB_VOLTAGE_UV_FAULT;
|
||||||
|
if (ret & VMON_OV_FAULT)
|
||||||
|
status |= PB_VOLTAGE_OV_FAULT;
|
||||||
|
ret = status;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = pmbus_read_byte_data(client, page, reg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
data->access = ktime_get();
|
data->access = ktime_get();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -111,13 +239,38 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg,
|
||||||
{
|
{
|
||||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||||
struct zl6100_data *data = to_zl6100_data(info);
|
struct zl6100_data *data = to_zl6100_data(info);
|
||||||
int ret;
|
int ret, vreg;
|
||||||
|
|
||||||
if (page || reg >= PMBUS_VIRT_BASE)
|
if (page > 0)
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
|
switch (reg) {
|
||||||
|
case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
|
||||||
|
word = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(word) * 10, 9));
|
||||||
|
vreg = MFR_VMON_OV_FAULT_LIMIT;
|
||||||
|
pmbus_clear_cache(client);
|
||||||
|
break;
|
||||||
|
case PMBUS_VIRT_VMON_OV_FAULT_LIMIT:
|
||||||
|
vreg = MFR_VMON_OV_FAULT_LIMIT;
|
||||||
|
pmbus_clear_cache(client);
|
||||||
|
break;
|
||||||
|
case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
|
||||||
|
word = zl6100_d2l(DIV_ROUND_CLOSEST(zl6100_l2d(word) * 10, 11));
|
||||||
|
vreg = MFR_VMON_UV_FAULT_LIMIT;
|
||||||
|
pmbus_clear_cache(client);
|
||||||
|
break;
|
||||||
|
case PMBUS_VIRT_VMON_UV_FAULT_LIMIT:
|
||||||
|
vreg = MFR_VMON_UV_FAULT_LIMIT;
|
||||||
|
pmbus_clear_cache(client);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (reg >= PMBUS_VIRT_BASE)
|
||||||
|
return -ENXIO;
|
||||||
|
vreg = reg;
|
||||||
|
}
|
||||||
|
|
||||||
zl6100_wait(data);
|
zl6100_wait(data);
|
||||||
ret = pmbus_write_word_data(client, page, reg, word);
|
ret = pmbus_write_word_data(client, page, vreg, word);
|
||||||
data->access = ktime_get();
|
data->access = ktime_get();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -225,6 +378,13 @@ static int zl6100_probe(struct i2c_client *client,
|
||||||
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
|
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
|
||||||
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
|
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ZL2004, ZL9101M, and ZL9117M support monitoring an extra voltage
|
||||||
|
* (VMON for ZL2004, VDRV for ZL9101M and ZL9117M). Report it as vmon.
|
||||||
|
*/
|
||||||
|
if (data->id == zl2004 || data->id == zl9101 || data->id == zl9117)
|
||||||
|
info->func[0] |= PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON;
|
||||||
|
|
||||||
ret = i2c_smbus_read_word_data(client, ZL6100_MFR_CONFIG);
|
ret = i2c_smbus_read_word_data(client, ZL6100_MFR_CONFIG);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Loading…
Add table
Reference in a new issue