Skip to content

Commit

Permalink
hwmon: (pmbus) Add support for VID output voltage mode
Browse files Browse the repository at this point in the history
In VID mode, output voltages are measured and reported as VID values, and
have to be converted to voltages using VID conversion tables or functions.
Support is added for VR11 only at this time.

This patch enables support for PMBus devices supporting VID VR11 based output
voltage selection such as NCP4200 and NCP4208.

Signed-off-by: Guenter Roeck <guenter.roeck@ericsson.com>
Reviewed-by: Robert Coulson <robert.coulson@ericsson.com>
  • Loading branch information
Guenter Roeck committed Jul 28, 2011
1 parent 9d2ecfb commit 1061d85
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 34 deletions.
6 changes: 3 additions & 3 deletions drivers/hwmon/pmbus/adm1275.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ static int adm1275_probe(struct i2c_client *client,
}

info->pages = 1;
info->direct[PSC_VOLTAGE_IN] = true;
info->direct[PSC_VOLTAGE_OUT] = true;
info->direct[PSC_CURRENT_OUT] = true;
info->format[PSC_VOLTAGE_IN] = direct;
info->format[PSC_VOLTAGE_OUT] = direct;
info->format[PSC_CURRENT_OUT] = direct;
info->m[PSC_CURRENT_OUT] = 807;
info->b[PSC_CURRENT_OUT] = 20475;
info->R[PSC_CURRENT_OUT] = -1;
Expand Down
6 changes: 3 additions & 3 deletions drivers/hwmon/pmbus/max16064.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@

static struct pmbus_driver_info max16064_info = {
.pages = 4,
.direct[PSC_VOLTAGE_IN] = true,
.direct[PSC_VOLTAGE_OUT] = true,
.direct[PSC_TEMPERATURE] = true,
.format[PSC_VOLTAGE_IN] = direct,
.format[PSC_VOLTAGE_OUT] = direct,
.format[PSC_TEMPERATURE] = direct,
.m[PSC_VOLTAGE_IN] = 19995,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = -1,
Expand Down
18 changes: 9 additions & 9 deletions drivers/hwmon/pmbus/max34440.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@ static int max34440_read_byte_data(struct i2c_client *client, int page, int reg)
static struct pmbus_driver_info max34440_info[] = {
[max34440] = {
.pages = 14,
.direct[PSC_VOLTAGE_IN] = true,
.direct[PSC_VOLTAGE_OUT] = true,
.direct[PSC_TEMPERATURE] = true,
.direct[PSC_CURRENT_OUT] = true,
.format[PSC_VOLTAGE_IN] = direct,
.format[PSC_VOLTAGE_OUT] = direct,
.format[PSC_TEMPERATURE] = direct,
.format[PSC_CURRENT_OUT] = direct,
.m[PSC_VOLTAGE_IN] = 1,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = 3, /* R = 0 in datasheet reflects mV */
Expand Down Expand Up @@ -112,11 +112,11 @@ static struct pmbus_driver_info max34440_info[] = {
},
[max34441] = {
.pages = 12,
.direct[PSC_VOLTAGE_IN] = true,
.direct[PSC_VOLTAGE_OUT] = true,
.direct[PSC_TEMPERATURE] = true,
.direct[PSC_CURRENT_OUT] = true,
.direct[PSC_FAN] = true,
.format[PSC_VOLTAGE_IN] = direct,
.format[PSC_VOLTAGE_OUT] = direct,
.format[PSC_TEMPERATURE] = direct,
.format[PSC_CURRENT_OUT] = direct,
.format[PSC_FAN] = direct,
.m[PSC_VOLTAGE_IN] = 1,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = 3,
Expand Down
8 changes: 4 additions & 4 deletions drivers/hwmon/pmbus/max8688.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ static int max8688_read_byte_data(struct i2c_client *client, int page, int reg)

static struct pmbus_driver_info max8688_info = {
.pages = 1,
.direct[PSC_VOLTAGE_IN] = true,
.direct[PSC_VOLTAGE_OUT] = true,
.direct[PSC_TEMPERATURE] = true,
.direct[PSC_CURRENT_OUT] = true,
.format[PSC_VOLTAGE_IN] = direct,
.format[PSC_VOLTAGE_OUT] = direct,
.format[PSC_TEMPERATURE] = direct,
.format[PSC_CURRENT_OUT] = direct,
.m[PSC_VOLTAGE_IN] = 19995,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = -1,
Expand Down
34 changes: 31 additions & 3 deletions drivers/hwmon/pmbus/pmbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ static void pmbus_find_sensor_groups(struct i2c_client *client,
static int pmbus_identify(struct i2c_client *client,
struct pmbus_driver_info *info)
{
int ret = 0;

if (!info->pages) {
/*
* Check if the PAGE command is supported. If it is,
Expand All @@ -117,6 +119,27 @@ static int pmbus_identify(struct i2c_client *client,
}
}

if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) {
int vout_mode;

vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE);
if (vout_mode >= 0 && vout_mode != 0xff) {
switch (vout_mode >> 5) {
case 0:
break;
case 1:
info->format[PSC_VOLTAGE_OUT] = vid;
break;
case 2:
info->format[PSC_VOLTAGE_OUT] = direct;
break;
default:
ret = -ENODEV;
goto abort;
}
}
}

/*
* We should check if the COEFFICIENTS register is supported.
* If it is, and the chip is configured for direct mode, we can read
Expand All @@ -125,13 +148,18 @@ static int pmbus_identify(struct i2c_client *client,
*
* To do this, we will need access to a chip which actually supports the
* COEFFICIENTS command, since the command is too complex to implement
* without testing it.
* without testing it. Until then, abort if a chip configured for direct
* mode was detected.
*/
if (info->format[PSC_VOLTAGE_OUT] == direct) {
ret = -ENODEV;
goto abort;
}

/* Try to find sensor groups */
pmbus_find_sensor_groups(client, info);

return 0;
abort:
return ret;
}

static int pmbus_probe(struct i2c_client *client,
Expand Down
7 changes: 4 additions & 3 deletions drivers/hwmon/pmbus/pmbus.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,11 +266,11 @@ enum pmbus_sensor_classes {
#define PMBUS_HAVE_STATUS_FAN12 (1 << 16)
#define PMBUS_HAVE_STATUS_FAN34 (1 << 17)

enum pmbus_data_format { linear = 0, direct, vid };

struct pmbus_driver_info {
int pages; /* Total number of pages */
bool direct[PSC_NUM_CLASSES];
/* true if device uses direct data format
for the given sensor class */
enum pmbus_data_format format[PSC_NUM_CLASSES];
/*
* Support one set of coefficients for each sensor type
* Used for chips providing data in direct mode.
Expand Down Expand Up @@ -299,6 +299,7 @@ struct pmbus_driver_info {

int pmbus_set_page(struct i2c_client *client, u8 page);
int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg);
int pmbus_read_byte_data(struct i2c_client *client, u8 page, u8 reg);
void pmbus_clear_faults(struct i2c_client *client);
bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg);
bool pmbus_check_word_register(struct i2c_client *client, int page, int reg);
Expand Down
60 changes: 51 additions & 9 deletions drivers/hwmon/pmbus/pmbus_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg)
}
EXPORT_SYMBOL_GPL(pmbus_read_word_data);

static int pmbus_read_byte_data(struct i2c_client *client, u8 page, u8 reg)
int pmbus_read_byte_data(struct i2c_client *client, u8 page, u8 reg)
{
int rv;

Expand All @@ -207,6 +207,7 @@ static int pmbus_read_byte_data(struct i2c_client *client, u8 page, u8 reg)

return i2c_smbus_read_byte_data(client, reg);
}
EXPORT_SYMBOL_GPL(pmbus_read_byte_data);

static void pmbus_clear_fault_page(struct i2c_client *client, int page)
{
Expand Down Expand Up @@ -443,15 +444,37 @@ static long pmbus_reg2data_direct(struct pmbus_data *data,
return (val - b) / m;
}

/*
* Convert VID sensor values to milli- or micro-units
* depending on sensor type.
* We currently only support VR11.
*/
static long pmbus_reg2data_vid(struct pmbus_data *data,
struct pmbus_sensor *sensor)
{
long val = sensor->data;

if (val < 0x02 || val > 0xb2)
return 0;
return DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100);
}

static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor)
{
long val;

if (data->info->direct[sensor->class])
switch (data->info->format[sensor->class]) {
case direct:
val = pmbus_reg2data_direct(data, sensor);
else
break;
case vid:
val = pmbus_reg2data_vid(data, sensor);
break;
case linear:
default:
val = pmbus_reg2data_linear(data, sensor);

break;
}
return val;
}

Expand Down Expand Up @@ -561,16 +584,31 @@ static u16 pmbus_data2reg_direct(struct pmbus_data *data,
return val;
}

static u16 pmbus_data2reg_vid(struct pmbus_data *data,
enum pmbus_sensor_classes class, long val)
{
val = SENSORS_LIMIT(val, 500, 1600);

return 2 + DIV_ROUND_CLOSEST((1600 - val) * 100, 625);
}

static u16 pmbus_data2reg(struct pmbus_data *data,
enum pmbus_sensor_classes class, long val)
{
u16 regval;

if (data->info->direct[class])
switch (data->info->format[class]) {
case direct:
regval = pmbus_data2reg_direct(data, class, val);
else
break;
case vid:
regval = pmbus_data2reg_vid(data, class, val);
break;
case linear:
default:
regval = pmbus_data2reg_linear(data, class, val);

break;
}
return regval;
}

Expand Down Expand Up @@ -1380,7 +1418,7 @@ static int pmbus_identify_common(struct i2c_client *client,
*/
switch (vout_mode >> 5) {
case 0: /* linear mode */
if (data->info->direct[PSC_VOLTAGE_OUT])
if (data->info->format[PSC_VOLTAGE_OUT] != linear)
return -ENODEV;

exponent = vout_mode & 0x1f;
Expand All @@ -1389,8 +1427,12 @@ static int pmbus_identify_common(struct i2c_client *client,
exponent |= ~0x1f;
data->exponent = exponent;
break;
case 1: /* VID mode */
if (data->info->format[PSC_VOLTAGE_OUT] != vid)
return -ENODEV;
break;
case 2: /* direct mode */
if (!data->info->direct[PSC_VOLTAGE_OUT])
if (data->info->format[PSC_VOLTAGE_OUT] != direct)
return -ENODEV;
break;
default:
Expand Down

0 comments on commit 1061d85

Please sign in to comment.