/* 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);
}
staticint zl6100_read_word_data(struct i2c_client *client, int page, int phase, int reg)
{ conststruct pmbus_driver_info *info = pmbus_get_driver_info(client); struct zl6100_data *data = to_zl6100_data(info); int ret, vreg;
if (page >= info->pages) return -ENXIO;
if (data->id == zl2005) { /* * Limit register detection is not reliable on ZL2005. * Make sure registers are not erroneously detected.
*/ switch (reg) { case PMBUS_VOUT_OV_WARN_LIMIT: case PMBUS_VOUT_UV_WARN_LIMIT: case PMBUS_IOUT_OC_WARN_LIMIT: return -ENXIO;
}
}
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;
}
ret = pmbus_read_word_data(client, page, phase, vreg); 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;
}
staticint zl6100_read_byte_data(struct i2c_client *client, int page, int reg)
{ conststruct pmbus_driver_info *info = pmbus_get_driver_info(client); int ret, status;
if (page >= info->pages) return -ENXIO;
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;
}
return ret;
}
staticint zl6100_write_word_data(struct i2c_client *client, int page, int reg,
u16 word)
{ conststruct pmbus_driver_info *info = pmbus_get_driver_info(client); int vreg;
if (page >= info->pages) 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;
}
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_WORD_DATA
| I2C_FUNC_SMBUS_READ_BLOCK_DATA)) return -ENODEV;
ret = i2c_smbus_read_block_data(client, ZL6100_DEVICE_ID,
device_id); if (ret < 0) {
dev_err(&client->dev, "Failed to read device ID\n"); return ret;
}
device_id[ret] = '\0';
dev_info(&client->dev, "Device ID %s\n", device_id);
mid = NULL; for (mid = zl6100_id; mid->name[0]; mid++) { if (!strncasecmp(mid->name, device_id, strlen(mid->name))) break;
} if (!mid->name[0]) {
dev_err(&client->dev, "Unsupported device\n"); return -ENODEV;
} if (strcmp(client->name, mid->name) != 0)
dev_notice(&client->dev, "Device mismatch: Configured %s, detected %s\n",
client->name, mid->name);
data = devm_kzalloc(&client->dev, sizeof(struct zl6100_data),
GFP_KERNEL); if (!data) return -ENOMEM;
data->id = mid->driver_data;
/* * According to information from the chip vendor, all currently * supported chips are known to require a wait time between I2C * accesses.
*/
udelay(delay);
/* * ZL2004, ZL8802, ZL9101M, ZL9117M and ZLS4009 support monitoring * an extra voltage (VMON for ZL2004, ZL8802 and ZLS4009, * VDRV for ZL9101M and ZL9117M). Report it as vmon.
*/ if (data->id == zl2004 || data->id == zl8802 || data->id == zl9101 ||
data->id == zl9117 || data->id == zls4009)
info->func[0] |= PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON;
/* * ZL8802 has two outputs that can be used either independently or in * a current sharing configuration. The driver uses the DDC_CONFIG * register to check if the module is running with independent or * shared outputs. If the module is in shared output mode, only one * output voltage will be reported.
*/ if (data->id == zl8802) {
info->pages = 2;
info->func[0] |= PMBUS_HAVE_IIN;
ret = i2c_smbus_read_word_data(client, ZL8802_MFR_DDC_CONFIG); if (ret < 0) return ret;
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.