/* * Number of additional attribute pointers to allocate * with each call to krealloc
*/ #define PMBUS_ATTR_ALLOC_SIZE 32 #define PMBUS_NAME_SIZE 24
/* * The type of operation used for picking the delay between * successive pmbus operations.
*/ #define PMBUS_OP_WRITE BIT(0) #define PMBUS_OP_PAGE_CHANGE BIT(1)
staticint wp = -1;
module_param(wp, int, 0444);
struct pmbus_sensor { struct pmbus_sensor *next; char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */ struct device_attribute attribute;
u8 page; /* page number */
u8 phase; /* phase number, 0xff for all phases */
u16 reg; /* register */ enum pmbus_sensor_classes class; /* sensor class */ bool update; /* runtime sensor update needed */ bool convert; /* Whether or not to apply linear/vid/direct */ int data; /* Sensor data; negative if there was a read error */
}; #define to_pmbus_sensor(_attr) \
container_of(_attr, struct pmbus_sensor, attribute)
/* * _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if * a device specific mapping function exists and calls it if necessary.
*/ staticint _pmbus_write_byte(struct i2c_client *client, int page, u8 value)
{ struct pmbus_data *data = i2c_get_clientdata(client); conststruct pmbus_driver_info *info = data->info; int status;
if (info->write_byte) {
status = info->write_byte(client, page, value); if (status != -ENODATA) return status;
} return pmbus_write_byte(client, page, value);
}
int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg,
u16 word)
{ int rv;
staticint pmbus_write_virt_reg(struct i2c_client *client, int page, int reg,
u16 word)
{ int bit; int id; int rv;
switch (reg) { case PMBUS_VIRT_FAN_TARGET_1 ... PMBUS_VIRT_FAN_TARGET_4:
id = reg - PMBUS_VIRT_FAN_TARGET_1;
bit = pmbus_fan_rpm_mask[id];
rv = pmbus_update_fan(client, page, id, bit, bit, word); break; default:
rv = -ENXIO; break;
}
return rv;
}
/* * _pmbus_write_word_data() is similar to pmbus_write_word_data(), but checks if * a device specific mapping function exists and calls it if necessary.
*/ staticint _pmbus_write_word_data(struct i2c_client *client, int page, int reg,
u16 word)
{ struct pmbus_data *data = i2c_get_clientdata(client); conststruct pmbus_driver_info *info = data->info; int status;
if (info->write_word_data) {
status = info->write_word_data(client, page, reg, word); if (status != -ENODATA) return status;
}
if (reg >= PMBUS_VIRT_BASE) return pmbus_write_virt_reg(client, page, reg, word);
/* * _pmbus_write_byte_data() is similar to pmbus_write_byte_data(), but checks if * a device specific mapping function exists and calls it if necessary.
*/ staticint _pmbus_write_byte_data(struct i2c_client *client, int page, int reg, u8 value)
{ struct pmbus_data *data = i2c_get_clientdata(client); conststruct pmbus_driver_info *info = data->info; int status;
if (info->write_byte_data) {
status = info->write_byte_data(client, page, reg, value); if (status != -ENODATA) return status;
} return pmbus_write_byte_data(client, page, reg, value);
}
/* * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if * a device specific mapping function exists and calls it if necessary.
*/ staticint _pmbus_read_byte_data(struct i2c_client *client, int page, int reg)
{ struct pmbus_data *data = i2c_get_clientdata(client); conststruct pmbus_driver_info *info = data->info; int status;
if (info->read_byte_data) {
status = info->read_byte_data(client, page, reg); if (status != -ENODATA) return status;
} return pmbus_read_byte_data(client, page, reg);
}
int pmbus_update_fan(struct i2c_client *client, int page, int id,
u8 config, u8 mask, u16 command)
{ int from; int rv;
u8 to;
from = _pmbus_read_byte_data(client, page,
pmbus_fan_config_registers[id]); if (from < 0) return from;
to = (from & ~mask) | (config & mask); if (to != from) {
rv = _pmbus_write_byte_data(client, page,
pmbus_fan_config_registers[id], to); if (rv < 0) return rv;
}
/* * _pmbus_read_word_data() is similar to pmbus_read_word_data(), but checks if * a device specific mapping function exists and calls it if necessary.
*/ staticint _pmbus_read_word_data(struct i2c_client *client, int page, int phase, int reg)
{ struct pmbus_data *data = i2c_get_clientdata(client); conststruct pmbus_driver_info *info = data->info; int status;
if (info->read_word_data) {
status = info->read_word_data(client, page, phase, reg); if (status != -ENODATA) return status;
}
if (reg >= PMBUS_VIRT_BASE) return pmbus_read_virt_reg(client, page, reg);
/* Same as above, but without phase parameter, for use in check functions */ staticint __pmbus_read_word_data(struct i2c_client *client, int page, int reg)
{ return _pmbus_read_word_data(client, page, 0xff, reg);
}
int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg)
{ int rv;
staticbool pmbus_check_register(struct i2c_client *client, int (*func)(struct i2c_client *client, int page, int reg), int page, int reg)
{ int rv; struct pmbus_data *data = i2c_get_clientdata(client);
/* * Convert ieee754 sensor values to milli- or micro-units * depending on sensor type. * * ieee754 data format: * bit 15: sign * bit 10..14: exponent * bit 0..9: mantissa * exponent=0: * v=(−1)^signbit * 2^(−14) * 0.significantbits * exponent=1..30: * v=(−1)^signbit * 2^(exponent - 15) * 1.significantbits * exponent=31: * v=NaN * * Add the number mantissa bits into the calculations for simplicity. * To do that, add '10' to the exponent. By doing that, we can just add * 0x400 to normal values and get the expected result.
*/ staticlong pmbus_reg2data_ieee754(struct pmbus_data *data, struct pmbus_sensor *sensor)
{ int exponent; bool sign; long val;
/* only support half precision for now */
sign = sensor->data & 0x8000;
exponent = (sensor->data >> 10) & 0x1f;
val = sensor->data & 0x3ff;
if (exponent == 0) { /* subnormal */
exponent = -(14 + 10);
} elseif (exponent == 0x1f) { /* NaN, convert to min/max */
exponent = 0;
val = 65504;
} else {
exponent -= (15 + 10); /* normal */
val |= 0x400;
}
/* scale result to milli-units for all sensors except fans */ if (sensor->class != PSC_FAN)
val = val * 1000L;
/* scale result to micro-units for power sensors */ if (sensor->class == PSC_POWER)
val = val * 1000L;
if (exponent >= 0)
val <<= exponent; else
val >>= -exponent;
if (sign)
val = -val;
return val;
}
/* * Convert linear sensor values to milli- or micro-units * depending on sensor type.
*/ static s64 pmbus_reg2data_linear(struct pmbus_data *data, struct pmbus_sensor *sensor)
{
s16 exponent;
s32 mantissa;
s64 val;
/* scale result to milli-units for all sensors except fans */ if (sensor->class != PSC_FAN)
val = val * 1000LL;
/* scale result to micro-units for power sensors */ if (sensor->class == PSC_POWER)
val = val * 1000LL;
if (exponent >= 0)
val <<= exponent; else
val >>= -exponent;
return val;
}
/* * Convert direct sensor values to milli- or micro-units * depending on sensor type.
*/ static s64 pmbus_reg2data_direct(struct pmbus_data *data, struct pmbus_sensor *sensor)
{
s64 b, val = (s16)sensor->data;
s32 m, R;
m = data->info->m[sensor->class];
b = data->info->b[sensor->class];
R = data->info->R[sensor->class];
if (m == 0) return 0;
/* X = 1/m * (Y * 10^-R - b) */
R = -R; /* scale result to milli-units for everything but fans */ if (!(sensor->class == PSC_FAN || sensor->class == PSC_PWM)) {
R += 3;
b *= 1000;
}
/* scale result to micro-units for power sensors */ if (sensor->class == PSC_POWER) {
R += 3;
b *= 1000;
}
while (R > 0) {
val *= 10;
R--;
} while (R < 0) {
val = div_s64(val + 5LL, 10L); /* round closest */
R++;
}
val = div_s64(val - b, m); return val;
}
/* * Convert VID sensor values to milli- or micro-units * depending on sensor type.
*/ static s64 pmbus_reg2data_vid(struct pmbus_data *data, struct pmbus_sensor *sensor)
{ long val = sensor->data; long rv = 0;
switch (data->info->vrm_version[sensor->page]) { case vr11: if (val >= 0x02 && val <= 0xb2)
rv = DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100); break; case vr12: if (val >= 0x01)
rv = 250 + (val - 1) * 5; break; case vr13: if (val >= 0x01)
rv = 500 + (val - 1) * 10; break; case imvp9: if (val >= 0x01)
rv = 200 + (val - 1) * 10; break; case amd625mv: if (val >= 0x0 && val <= 0xd8)
rv = DIV_ROUND_CLOSEST(155000 - val * 625, 100); break;
} return rv;
}
switch (data->info->format[sensor->class]) { case direct:
val = pmbus_reg2data_direct(data, sensor); break; case vid:
val = pmbus_reg2data_vid(data, sensor); break; case ieee754:
val = pmbus_reg2data_ieee754(data, sensor); break; case linear: default:
val = pmbus_reg2data_linear(data, sensor); break;
} return val;
}
/* Power is in uW. Convert to mW before converting. */ if (sensor->class == PSC_POWER)
val = DIV_ROUND_CLOSEST(val, 1000L);
/* * For simplicity, convert fan data to milli-units * before calculating the exponent.
*/ if (sensor->class == PSC_FAN)
val = val * 1000;
/* Reduce large mantissa until it fits into 10 bit */ while (val > MAX_IEEE_MANTISSA && exponent < 30) {
exponent++;
val >>= 1;
} /* * Increase small mantissa to generate valid 'normal' * number
*/ while (val < MIN_IEEE_MANTISSA && exponent > 1) {
exponent--;
val <<= 1;
}
/* Convert mantissa from milli-units to units */
mantissa = DIV_ROUND_CLOSEST(val, 1000);
/* * Ensure that the resulting number is within range. * Valid range is 0x400..0x7ff, where bit 10 reflects * the implied high bit in normalized ieee754 numbers. * Set the range to 0x400..0x7ff to reflect this. * The upper bit is then removed by the mask against * 0x3ff in the final assignment.
*/ if (mantissa > 0x7ff)
mantissa = 0x7ff; elseif (mantissa < 0x400)
mantissa = 0x400;
/* Convert to sign, 5 bit exponent, 10 bit mantissa */ return sign | (mantissa & 0x3ff) | ((exponent << 10) & 0x7c00);
}
if (sensor->class == PSC_VOLTAGE_OUT) { /* LINEAR16 does not support negative voltages */ if (val < 0) return 0;
/* * For a static exponents, we don't have a choice * but to adjust the value to it.
*/ if (data->exponent[sensor->page] < 0)
val <<= -data->exponent[sensor->page]; else
val >>= data->exponent[sensor->page];
val = DIV_ROUND_CLOSEST_ULL(val, 1000); return clamp_val(val, 0, 0xffff);
}
if (val < 0) {
negative = true;
val = -val;
}
/* Power is in uW. Convert to mW before converting. */ if (sensor->class == PSC_POWER)
val = DIV_ROUND_CLOSEST_ULL(val, 1000);
/* * For simplicity, convert fan data to milli-units * before calculating the exponent.
*/ if (sensor->class == PSC_FAN)
val = val * 1000LL;
/* Reduce large mantissa until it fits into 10 bit */ while (val >= MAX_LIN_MANTISSA && exponent < 15) {
exponent++;
val >>= 1;
} /* Increase small mantissa to improve precision */ while (val < MIN_LIN_MANTISSA && exponent > -15) {
exponent--;
val <<= 1;
}
/* Convert mantissa from milli-units to units */
mantissa = clamp_val(DIV_ROUND_CLOSEST_ULL(val, 1000), 0, 0x3ff);
/* restore sign */ if (negative)
mantissa = -mantissa;
/* Convert to 5 bit exponent, 11 bit mantissa */ return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800);
}
m = data->info->m[sensor->class];
b = data->info->b[sensor->class];
R = data->info->R[sensor->class];
/* Power is in uW. Adjust R and b. */ if (sensor->class == PSC_POWER) {
R -= 3;
b *= 1000;
}
/* Calculate Y = (m * X + b) * 10^R */ if (!(sensor->class == PSC_FAN || sensor->class == PSC_PWM)) {
R -= 3; /* Adjust R and b for data in milli-units */
b *= 1000;
}
val = val * m + b;
while (R > 0) {
val *= 10;
R--;
} while (R < 0) {
val = div_s64(val + 5LL, 10L); /* round closest */
R++;
}
switch (data->info->format[sensor->class]) { case direct:
regval = pmbus_data2reg_direct(data, sensor, val); break; case vid:
regval = pmbus_data2reg_vid(data, sensor, val); break; case ieee754:
regval = pmbus_data2reg_ieee754(data, sensor, val); break; case linear: default:
regval = pmbus_data2reg_linear(data, sensor, val); break;
} return regval;
}
/* * Return boolean calculated from converted data. * <index> defines a status register index and mask. * The mask is in the lower 8 bits, the register index is in bits 8..23. * * The associated pmbus_boolean structure contains optional pointers to two * sensor attributes. If specified, those attributes are compared against each * other to determine if a limit has been exceeded. * * If the sensor attribute pointers are NULL, the function returns true if * (status[reg] & mask) is true. * * If sensor attribute pointers are provided, a comparison against a specified * limit has to be performed to determine the boolean result. * In this case, the function returns true if v1 >= v2 (where v1 and v2 are * sensor values referenced by sensor attribute pointers s1 and s2). * * To determine if an object exceeds upper limits, specify <s1,s2> = <v,limit>. * To determine if an object exceeds lower limits, specify <s1,s2> = <limit,v>. * * If a negative value is stored in any of the referenced registers, this value * reflects an error code which will be returned.
*/ staticint pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b, int index)
{ struct pmbus_data *data = i2c_get_clientdata(client); struct pmbus_sensor *s1 = b->s1; struct pmbus_sensor *s2 = b->s2;
u16 mask = pb_index_to_mask(index);
u8 page = pb_index_to_page(index);
u16 reg = pb_index_to_reg(index); int ret, status;
u16 regval;
mutex_lock(&data->update_lock);
status = pmbus_get_status(client, page, reg); if (status < 0) {
ret = status; goto unlock;
}
if (s1)
pmbus_update_sensor_data(client, s1); if (s2)
pmbus_update_sensor_data(client, s2);
regval = status & mask; if (regval) { if (data->revision >= PMBUS_REV_12) {
ret = _pmbus_write_byte_data(client, page, reg, regval); if (ret) goto unlock;
} else {
pmbus_clear_fault_page(client, page);
}
} if (s1 && s2) {
s64 v1, v2;
if (s1->data < 0) {
ret = s1->data; goto unlock;
} if (s2->data < 0) {
ret = s2->data; goto unlock;
}
tzd = devm_thermal_of_zone_register(dev, index, tdata,
&pmbus_thermal_ops); /* * If CONFIG_THERMAL_OF is disabled, this returns -ENODEV, * so ignore that error but forward any other error.
*/ if (IS_ERR(tzd) && (PTR_ERR(tzd) != -ENODEV)) return PTR_ERR(tzd);
return 0;
}
staticstruct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data, constchar *name, constchar *type, int seq, int page, int phase, int reg, enum pmbus_sensor_classes class, bool update, bool readonly, bool convert)
{ struct pmbus_sensor *sensor; struct device_attribute *a;
sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL); if (!sensor) return NULL;
a = &sensor->attribute;
/* temperature sensors with _input values are registered with thermal */ if (class == PSC_TEMPERATURE && strcmp(type, "input") == 0)
pmbus_thermal_add_sensor(data, sensor, seq);
return sensor;
}
staticint pmbus_add_label(struct pmbus_data *data, constchar *name, int seq, constchar *lstring, int index, int phase)
{ struct pmbus_label *label; struct device_attribute *a;
label = devm_kzalloc(data->dev, sizeof(*label), GFP_KERNEL); if (!label) return -ENOMEM;
/* * Search for attributes. Allocate sensors, booleans, and labels as needed.
*/
/* * The pmbus_limit_attr structure describes a single limit attribute * and its associated alarm attribute.
*/ struct pmbus_limit_attr {
u16 reg; /* Limit register */
u16 sbit; /* Alarm attribute status bit */ bool update; /* True if register needs updates */ bool low; /* True if low limit; for limits with compare functions only */ constchar *attr; /* Attribute name */ constchar *alarm; /* Alarm attribute name */
};
/* * The pmbus_sensor_attr structure describes one sensor attribute. This * description includes a reference to the associated limit attributes.
*/ struct pmbus_sensor_attr {
u16 reg; /* sensor register */
u16 gbit; /* generic status bit */
u8 nlimit; /* # of limit registers */ enum pmbus_sensor_classes class;/* sensor class */ constchar *label; /* sensor label */ bool paged; /* true if paged sensor */ bool update; /* true if update needed */ bool compare; /* true if compare function needed */
u32 func; /* sensor mask */
u32 sfunc; /* sensor status mask */ int sreg; /* status register */ conststruct pmbus_limit_attr *limit;/* limit registers */
};
/* * Add a set of limit attributes and, if supported, the associated * alarm attributes. * returns 0 if no alarm register found, 1 if an alarm register was found, * < 0 on errors.
*/ staticint pmbus_add_limit_attrs(struct i2c_client *client, struct pmbus_data *data, conststruct pmbus_driver_info *info, constchar *name, int index, int page, struct pmbus_sensor *base, conststruct pmbus_sensor_attr *attr)
{ conststruct pmbus_limit_attr *l = attr->limit; int nlimit = attr->nlimit; int have_alarm = 0; int i, ret; struct pmbus_sensor *curr;
for (i = 0; i < nlimit; i++) { if (pmbus_check_word_register(client, page, l->reg)) {
curr = pmbus_add_sensor(data, name, l->attr, index,
page, 0xff, l->reg, attr->class,
attr->update || l->update, false, true); if (!curr) return -ENOMEM; if (l->sbit && (info->func[page] & attr->sfunc)) {
ret = pmbus_add_boolean(data, name,
l->alarm, index,
attr->compare ? l->low ? curr : base
: NULL,
attr->compare ? l->low ? base : curr
: NULL,
page, attr->sreg, l->sbit); if (ret) return ret;
have_alarm = 1;
}
}
l++;
} return have_alarm;
}
staticint pmbus_add_sensor_attrs_one(struct i2c_client *client, struct pmbus_data *data, conststruct pmbus_driver_info *info, constchar *name, int index, int page, int phase, conststruct pmbus_sensor_attr *attr, bool paged)
{ struct pmbus_sensor *base; bool upper = !!(attr->gbit & 0xff00); /* need to check STATUS_WORD */ int ret;
if (attr->label) {
ret = pmbus_add_label(data, name, index, attr->label,
paged ? page + 1 : 0, phase); if (ret) return ret;
}
base = pmbus_add_sensor(data, name, "input", index, page, phase,
attr->reg, attr->class, true, true, true); if (!base) return -ENOMEM; /* No limit and alarm attributes for phase specific sensors */ if (attr->sfunc && phase == 0xff) {
ret = pmbus_add_limit_attrs(client, data, info, name,
index, page, base, attr); if (ret < 0) return ret; /* * Add generic alarm attribute only if there are no individual * alarm attributes, if there is a global alarm bit, and if * the generic status register (word or byte, depending on * which global bit is set) for this page is accessible.
*/ if (!ret && attr->gbit &&
(!upper || data->has_status_word) &&
pmbus_check_status_register(client, page)) {
ret = pmbus_add_boolean(data, name, "alarm", index,
NULL, NULL,
page, PMBUS_STATUS_WORD,
attr->gbit); if (ret) return ret;
}
} return 0;
}
staticbool pmbus_sensor_is_paged(conststruct pmbus_driver_info *info, conststruct pmbus_sensor_attr *attr)
{ int p;
if (attr->paged) returntrue;
/* * Some attributes may be present on more than one page despite * not being marked with the paged attribute. If that is the case, * then treat the sensor as being paged and add the page suffix to the * attribute name. * We don't just add the paged attribute to all such attributes, in * order to maintain the un-suffixed labels in the case where the * attribute is only on page 0.
*/ for (p = 1; p < info->pages; p++) { if (info->func[p] & attr->func) returntrue;
} returnfalse;
}
staticint pmbus_add_sensor_attrs(struct i2c_client *client, struct pmbus_data *data, constchar *name, conststruct pmbus_sensor_attr *attrs, int nattrs)
{ conststruct pmbus_driver_info *info = data->info; int index, i; int ret;
index = 1; for (i = 0; i < nattrs; i++) { int page, pages; bool paged = pmbus_sensor_is_paged(info, attrs);
pages = paged ? info->pages : 1; for (page = 0; page < pages; page++) { if (info->func[page] & attrs->func) {
ret = pmbus_add_sensor_attrs_one(client, data, info,
name, index, page,
0xff, attrs, paged); if (ret) return ret;
index++;
} if (info->phases[page]) { int phase;
for (phase = 0; phase < info->phases[page];
phase++) { if (!(info->pfunc[phase] & attrs->func)) continue;
ret = pmbus_add_sensor_attrs_one(client,
data, info, name, index, page,
phase, attrs, paged); if (ret) return ret;
index++;
}
}
}
attrs++;
} return 0;
}
/* Precondition: FAN_CONFIG_x_y and FAN_COMMAND_x must exist for the fan ID */ staticint pmbus_add_fan_ctrl(struct i2c_client *client, struct pmbus_data *data, int index, int page, int id, u8 config)
{ struct pmbus_sensor *sensor;
staticint pmbus_add_fan_attributes(struct i2c_client *client, struct pmbus_data *data)
{ conststruct pmbus_driver_info *info = data->info; int index = 1; int page; int ret;
for (page = 0; page < info->pages; page++) { int f;
for (f = 0; f < ARRAY_SIZE(pmbus_fan_registers); f++) { int regval;
if (!(info->func[page] & pmbus_fan_flags[f])) break;
if (!pmbus_check_word_register(client, page,
pmbus_fan_registers[f])) break;
/* * Skip fan if not installed. * Each fan configuration register covers multiple fans, * so we have to do some magic.
*/
regval = _pmbus_read_byte_data(client, page,
pmbus_fan_config_registers[f]); if (regval < 0 ||
(!(regval & (PB_FAN_1_INSTALLED >> ((f & 1) * 4))))) continue;
/* Fan control */ if (pmbus_check_word_register(client, page,
pmbus_fan_command_registers[f])) {
ret = pmbus_add_fan_ctrl(client, data, index,
page, f, regval); if (ret < 0) return ret;
}
/* * Each fan status register covers multiple fans, * so we have to do some magic.
*/ if ((info->func[page] & pmbus_fan_status_flags[f]) &&
pmbus_check_byte_register(client,
page, pmbus_fan_status_registers[f])) { int reg;
/* * Read the coefficients for direct mode.
*/ staticint pmbus_read_coefficients(struct i2c_client *client, struct pmbus_driver_info *info, conststruct pmbus_sensor_attr *attr)
{ int rv; union i2c_smbus_data data; enum pmbus_sensor_classes class = attr->class;
s8 R;
s16 m, b;
m = data.block[1] | (data.block[2] << 8);
b = data.block[3] | (data.block[4] << 8);
R = data.block[5];
info->m[class] = m;
info->b[class] = b;
info->R[class] = R;
return rv;
}
staticint pmbus_init_coefficients(struct i2c_client *client, struct pmbus_driver_info *info)
{ int i, n, ret = -EINVAL; conststruct pmbus_class_attr_map *map; conststruct pmbus_sensor_attr *attr;
for (i = 0; i < ARRAY_SIZE(class_attr_map); i++) {
map = &class_attr_map[i]; if (info->format[map->class] != direct) continue; for (n = 0; n < map->nattr; n++) {
attr = &map->attr[n]; if (map->class != attr->class) continue;
ret = pmbus_read_coefficients(client, info, attr); if (ret >= 0) break;
} if (ret < 0) {
dev_err(&client->dev, "No coefficients found for sensor class %d\n",
map->class); return -EINVAL;
}
}
return 0;
}
/* * Identify chip parameters. * This function is called for all chips.
*/ staticint pmbus_identify_common(struct i2c_client *client, struct pmbus_data *data, int page)
{ int vout_mode = -1;
if (pmbus_check_byte_register(client, page, PMBUS_VOUT_MODE))
vout_mode = _pmbus_read_byte_data(client, page,
PMBUS_VOUT_MODE); if (vout_mode >= 0 && vout_mode != 0xff) { /* * Not all chips support the VOUT_MODE command, * so a failure to read it is not an error.
*/ switch (vout_mode >> 5) { case 0: /* linear mode */ if (data->info->format[PSC_VOLTAGE_OUT] != linear) return -ENODEV;
data->exponent[page] = ((s8)(vout_mode << 3)) >> 3; break; case 1: /* VID mode */ if (data->info->format[PSC_VOLTAGE_OUT] != vid) return -ENODEV; break; case 2: /* direct mode */ if (data->info->format[PSC_VOLTAGE_OUT] != direct) return -ENODEV; break; case 3: /* ieee 754 half precision */ if (data->info->format[PSC_VOLTAGE_OUT] != ieee754) return -ENODEV; break; default: return -ENODEV;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.