// SPDX-License-Identifier: GPL-2.0 /* * iio/adc/max9611.c * * Maxim max9611/max9612 high side current sense amplifier with * 12-bit ADC interface. * * Copyright (C) 2017 Jacopo Mondi
*/
/* * This driver supports input common-mode voltage, current-sense * amplifier with programmable gains and die temperature reading from * Maxim max9611/max9612. * * Op-amp, analog comparator, and watchdog functionalities are not * supported by this driver.
*/
/* * max9611 common input mode (CIM): LSB is 14mV, with 14mV offset at 25 C * * The complete formula to calculate input common voltage is: * (((adc_read >> 4) * 1000) - offset) / (1 / 14 * 1000)
*/ #define MAX9611_CIM_LSB_mV 14 #define MAX9611_CIM_OFFSET_RAW 1
/* * max9611 temperature reading: LSB is 480 milli degrees Celsius * * The complete formula to calculate temperature is: * ((adc_read >> 7) * 1000) / (1 / 480 * 1000)
*/ #define MAX9611_TEMP_MAX_POS 0x7f80 #define MAX9611_TEMP_MAX_NEG 0xff80 #define MAX9611_TEMP_MIN_NEG 0xd980 #define MAX9611_TEMP_MASK GENMASK(15, 7) #define MAX9611_TEMP_SHIFT 0x07 #define MAX9611_TEMP_RAW(_r) ((_r) >> MAX9611_TEMP_SHIFT) #define MAX9611_TEMP_SCALE_NUM 1000000 #define MAX9611_TEMP_SCALE_DIV 2083
/* * Conversion time is 2 ms (typically) at Ta=25 degreeC * No maximum value is known, so play it safe.
*/ #define MAX9611_CONV_TIME_US_RANGE 3000, 3300
/* * max9611_csa_gain_conf - associate gain multiplier with LSB and * offset values. * * Group together parameters associated with configurable gain * on current sense amplifier path to ADC interface. * Current sense read routine adjusts gain until it gets a meaningful * value; use this structure to retrieve the correct LSB and offset values.
*/ staticconstunsignedint max9611_gain_conf[][2] = {
[CSA_GAIN_1x] = { MAX9611_CSA_1X_LSB_nV, MAX9611_CSA_1X_OFFS_RAW, },
[CSA_GAIN_4x] = { MAX9611_CSA_4X_LSB_nV, MAX9611_CSA_4X_OFFS_RAW, },
[CSA_GAIN_8x] = { MAX9611_CSA_8X_LSB_nV, MAX9611_CSA_8X_OFFS_RAW, },
};
/** * max9611_read_single() - read a single value from ADC interface * * Data registers are 16 bit long, spread between two 8 bit registers * with consecutive addresses. * Configure ADC mux first, then read register at address "reg_addr". * The smbus_read_word routine asks for 16 bits and the ADC is kind enough * to return values from "reg_addr" and "reg_addr + 1" consecutively. * Data are transmitted with big-endian ordering: MSB arrives first. * * @max9611: max9611 device * @selector: index for mux and register configuration * @raw_val: the value returned from ADC
*/ staticint max9611_read_single(struct max9611_dev *max9611, enum max9611_conf_ids selector,
u16 *raw_val)
{ int ret;
/* * Keep mutex lock held during read-write to avoid mux register * (CTRL1) re-configuration.
*/
mutex_lock(&max9611->lock);
ret = i2c_smbus_write_byte_data(max9611->i2c_client,
MAX9611_REG_CTRL1, mux_conf); if (ret) {
dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n",
MAX9611_REG_CTRL1, mux_conf);
mutex_unlock(&max9611->lock); return ret;
}
/* need a delay here to make register configuration stabilize. */
usleep_range(MAX9611_CONV_TIME_US_RANGE);
ret = i2c_smbus_read_word_swapped(max9611->i2c_client, reg_addr); if (ret < 0) {
dev_err(max9611->dev, "i2c read word from 0x%2x failed\n",
reg_addr);
mutex_unlock(&max9611->lock); return ret;
}
*raw_val = ret;
mutex_unlock(&max9611->lock);
return 0;
}
/** * max9611_read_csa_voltage() - read current sense amplifier output voltage * * Current sense amplifier output voltage is read through a configurable * 1x, 4x or 8x gain. * Start with plain 1x gain, and adjust gain control properly until a * meaningful value is read from ADC output. * * @max9611: max9611 device * @adc_raw: raw value read from ADC output * @csa_gain: gain configuration option selector
*/ staticint max9611_read_csa_voltage(struct max9611_dev *max9611,
u16 *adc_raw, enum max9611_csa_gain *csa_gain)
{ enum max9611_conf_ids gain_selectors[] = {
CONF_SENSE_1x,
CONF_SENSE_4x,
CONF_SENSE_8x
}; unsignedint i; int ret;
for (i = 0; i < ARRAY_SIZE(gain_selectors); ++i) {
ret = max9611_read_single(max9611, gain_selectors[i], adc_raw); if (ret) return ret;
case MAX9611_CHAN_VOLTAGE_INPUT:
*val = MAX9611_CIM_LSB_mV;
return IIO_VAL_INT;
}
break;
case IIO_CHAN_INFO_PROCESSED:
switch (chan->address) { case MAX9611_CHAN_VOLTAGE_SENSE: /* * processed (mV): (raw - offset) * LSB (nV) / 10^6 * * Even if max9611 can output raw csa voltage readings, * use a produced value as scale depends on gain.
*/
ret = max9611_read_csa_voltage(dev, &adc_data,
&gain_selector); if (ret) return -EINVAL;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WRITE_BYTE |
I2C_FUNC_SMBUS_READ_WORD_DATA)) {
dev_err(max9611->dev, "I2c adapter does not support smbus write_byte or read_word functionalities: aborting probe.\n"); return -EINVAL;
}
/* Make sure die temperature is in range to test communications. */
ret = max9611_read_single(max9611, CONF_TEMP, ®val); if (ret) return ret;
regval &= MAX9611_TEMP_MASK;
if ((regval > MAX9611_TEMP_MAX_POS &&
regval < MAX9611_TEMP_MIN_NEG) ||
regval > MAX9611_TEMP_MAX_NEG) {
dev_err(max9611->dev, "Invalid value received from ADC 0x%4x: aborting\n",
regval); return -EIO;
}
/* Mux shall be zeroed back before applying other configurations */
ret = i2c_smbus_write_byte_data(max9611->i2c_client,
MAX9611_REG_CTRL1, 0); if (ret) {
dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n",
MAX9611_REG_CTRL1, 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.