iio: temperature: ltc2983: support more parts
Add support for the following parts: * LTC2984 * LTC2986 * LTM2985 The LTC2984 is a variant of the LTC2983 with EEPROM. The LTC2986 is a variant of the LTC2983 with only 10 channels, EEPROM and support for active analog temperature sensors. The LTM2985 is software-compatible with the LTC2986. Signed-off-by: Cosmin Tanislav <cosmin.tanislav@analog.com> Link: https://lore.kernel.org/r/20221103130041.2153295-14-demonsingur@gmail.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
This commit is contained in:
Родитель
d240526950
Коммит
6f7cadcf66
|
@ -25,9 +25,12 @@
|
|||
#define LTC2983_STATUS_REG 0x0000
|
||||
#define LTC2983_TEMP_RES_START_REG 0x0010
|
||||
#define LTC2983_TEMP_RES_END_REG 0x005F
|
||||
#define LTC2983_EEPROM_KEY_REG 0x00B0
|
||||
#define LTC2983_EEPROM_READ_STATUS_REG 0x00D0
|
||||
#define LTC2983_GLOBAL_CONFIG_REG 0x00F0
|
||||
#define LTC2983_MULT_CHANNEL_START_REG 0x00F4
|
||||
#define LTC2983_MULT_CHANNEL_END_REG 0x00F7
|
||||
#define LTC2986_EEPROM_STATUS_REG 0x00F9
|
||||
#define LTC2983_MUX_CONFIG_REG 0x00FF
|
||||
#define LTC2983_CHAN_ASSIGN_START_REG 0x0200
|
||||
#define LTC2983_CHAN_ASSIGN_END_REG 0x024F
|
||||
|
@ -35,13 +38,21 @@
|
|||
#define LTC2983_CUST_SENS_TBL_END_REG 0x03CF
|
||||
|
||||
#define LTC2983_DIFFERENTIAL_CHAN_MIN 2
|
||||
#define LTC2983_MAX_CHANNELS_NR 20
|
||||
#define LTC2983_MIN_CHANNELS_NR 1
|
||||
#define LTC2983_SLEEP 0x97
|
||||
#define LTC2983_CUSTOM_STEINHART_SIZE 24
|
||||
#define LTC2983_CUSTOM_SENSOR_ENTRY_SZ 6
|
||||
#define LTC2983_CUSTOM_STEINHART_ENTRY_SZ 4
|
||||
|
||||
#define LTC2983_EEPROM_KEY 0xA53C0F5A
|
||||
#define LTC2983_EEPROM_WRITE_CMD 0x15
|
||||
#define LTC2983_EEPROM_READ_CMD 0x16
|
||||
#define LTC2983_EEPROM_STATUS_FAILURE_MASK GENMASK(3, 1)
|
||||
#define LTC2983_EEPROM_READ_FAILURE_MASK GENMASK(7, 0)
|
||||
|
||||
#define LTC2983_EEPROM_WRITE_TIME_MS 2600
|
||||
#define LTC2983_EEPROM_READ_TIME_MS 20
|
||||
|
||||
#define LTC2983_CHAN_START_ADDR(chan) \
|
||||
(((chan - 1) * 4) + LTC2983_CHAN_ASSIGN_START_REG)
|
||||
#define LTC2983_CHAN_RES_ADDR(chan) \
|
||||
|
@ -171,6 +182,7 @@ enum {
|
|||
LTC2983_SENSOR_DIODE = 28,
|
||||
LTC2983_SENSOR_SENSE_RESISTOR = 29,
|
||||
LTC2983_SENSOR_DIRECT_ADC = 30,
|
||||
LTC2983_SENSOR_ACTIVE_TEMP = 31,
|
||||
};
|
||||
|
||||
#define to_thermocouple(_sensor) \
|
||||
|
@ -191,7 +203,17 @@ enum {
|
|||
#define to_adc(_sensor) \
|
||||
container_of(_sensor, struct ltc2983_adc, sensor)
|
||||
|
||||
#define to_temp(_sensor) \
|
||||
container_of(_sensor, struct ltc2983_temp, sensor)
|
||||
|
||||
struct ltc2983_chip_info {
|
||||
unsigned int max_channels_nr;
|
||||
bool has_temp;
|
||||
bool has_eeprom;
|
||||
};
|
||||
|
||||
struct ltc2983_data {
|
||||
const struct ltc2983_chip_info *info;
|
||||
struct regmap *regmap;
|
||||
struct spi_device *spi;
|
||||
struct mutex lock;
|
||||
|
@ -210,6 +232,7 @@ struct ltc2983_data {
|
|||
*/
|
||||
__be32 temp __aligned(IIO_DMA_MINALIGN);
|
||||
__be32 chan_val;
|
||||
__be32 eeprom_key;
|
||||
};
|
||||
|
||||
struct ltc2983_sensor {
|
||||
|
@ -272,6 +295,12 @@ struct ltc2983_adc {
|
|||
bool single_ended;
|
||||
};
|
||||
|
||||
struct ltc2983_temp {
|
||||
struct ltc2983_sensor sensor;
|
||||
struct ltc2983_custom_sensor *custom;
|
||||
bool single_ended;
|
||||
};
|
||||
|
||||
/*
|
||||
* Convert to Q format numbers. These number's are integers where
|
||||
* the number of integer and fractional bits are specified. The resolution
|
||||
|
@ -606,6 +635,22 @@ static int ltc2983_adc_assign_chan(struct ltc2983_data *st,
|
|||
return __ltc2983_chan_assign_common(st, sensor, chan_val);
|
||||
}
|
||||
|
||||
static int ltc2983_temp_assign_chan(struct ltc2983_data *st,
|
||||
const struct ltc2983_sensor *sensor)
|
||||
{
|
||||
struct ltc2983_temp *temp = to_temp(sensor);
|
||||
u32 chan_val;
|
||||
int ret;
|
||||
|
||||
chan_val = LTC2983_ADC_SINGLE_ENDED(temp->single_ended);
|
||||
|
||||
ret = __ltc2983_chan_custom_sensor_assign(st, temp->custom, &chan_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return __ltc2983_chan_assign_common(st, sensor, chan_val);
|
||||
}
|
||||
|
||||
static struct ltc2983_sensor *
|
||||
ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data *st,
|
||||
const struct ltc2983_sensor *sensor)
|
||||
|
@ -771,10 +816,10 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
|
|||
if (rtd->sensor_config & LTC2983_RTD_4_WIRE_MASK) {
|
||||
/* 4-wire */
|
||||
u8 min = LTC2983_DIFFERENTIAL_CHAN_MIN,
|
||||
max = LTC2983_MAX_CHANNELS_NR;
|
||||
max = st->info->max_channels_nr;
|
||||
|
||||
if (rtd->sensor_config & LTC2983_RTD_ROTATION_MASK)
|
||||
max = LTC2983_MAX_CHANNELS_NR - 1;
|
||||
max = st->info->max_channels_nr - 1;
|
||||
|
||||
if (((rtd->sensor_config & LTC2983_RTD_KELVIN_R_SENSE_MASK)
|
||||
== LTC2983_RTD_KELVIN_R_SENSE_MASK) &&
|
||||
|
@ -1143,6 +1188,38 @@ static struct ltc2983_sensor *ltc2983_adc_new(struct fwnode_handle *child,
|
|||
return &adc->sensor;
|
||||
}
|
||||
|
||||
static struct ltc2983_sensor *ltc2983_temp_new(struct fwnode_handle *child,
|
||||
struct ltc2983_data *st,
|
||||
const struct ltc2983_sensor *sensor)
|
||||
{
|
||||
struct ltc2983_temp *temp;
|
||||
|
||||
temp = devm_kzalloc(&st->spi->dev, sizeof(*temp), GFP_KERNEL);
|
||||
if (!temp)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (fwnode_property_read_bool(child, "adi,single-ended"))
|
||||
temp->single_ended = true;
|
||||
|
||||
if (!temp->single_ended &&
|
||||
sensor->chan < LTC2983_DIFFERENTIAL_CHAN_MIN) {
|
||||
dev_err(&st->spi->dev, "Invalid chan:%d for differential temp\n",
|
||||
sensor->chan);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
temp->custom = __ltc2983_custom_sensor_new(st, child, "adi,custom-temp",
|
||||
false, 4096, true);
|
||||
if (IS_ERR(temp->custom))
|
||||
return ERR_CAST(temp->custom);
|
||||
|
||||
/* set common parameters */
|
||||
temp->sensor.assign_chan = ltc2983_temp_assign_chan;
|
||||
temp->sensor.fault_handler = ltc2983_common_fault_handler;
|
||||
|
||||
return &temp->sensor;
|
||||
}
|
||||
|
||||
static int ltc2983_chan_read(struct ltc2983_data *st,
|
||||
const struct ltc2983_sensor *sensor, int *val)
|
||||
{
|
||||
|
@ -1302,10 +1379,10 @@ static int ltc2983_parse_dt(struct ltc2983_data *st)
|
|||
|
||||
/* check if we have a valid channel */
|
||||
if (sensor.chan < LTC2983_MIN_CHANNELS_NR ||
|
||||
sensor.chan > LTC2983_MAX_CHANNELS_NR) {
|
||||
sensor.chan > st->info->max_channels_nr) {
|
||||
ret = -EINVAL;
|
||||
dev_err(dev, "chan:%d must be from %u to %u\n", sensor.chan,
|
||||
LTC2983_MIN_CHANNELS_NR, LTC2983_MAX_CHANNELS_NR);
|
||||
LTC2983_MIN_CHANNELS_NR, st->info->max_channels_nr);
|
||||
goto put_child;
|
||||
} else if (channel_avail_mask & BIT(sensor.chan)) {
|
||||
ret = -EINVAL;
|
||||
|
@ -1345,6 +1422,9 @@ static int ltc2983_parse_dt(struct ltc2983_data *st)
|
|||
st->iio_channels--;
|
||||
} else if (sensor.type == LTC2983_SENSOR_DIRECT_ADC) {
|
||||
st->sensors[chan] = ltc2983_adc_new(child, st, &sensor);
|
||||
} else if (st->info->has_temp &&
|
||||
sensor.type == LTC2983_SENSOR_ACTIVE_TEMP) {
|
||||
st->sensors[chan] = ltc2983_temp_new(child, st, &sensor);
|
||||
} else {
|
||||
dev_err(dev, "Unknown sensor type %d\n", sensor.type);
|
||||
ret = -EINVAL;
|
||||
|
@ -1371,6 +1451,45 @@ put_child:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int ltc2983_eeprom_cmd(struct ltc2983_data *st, unsigned int cmd,
|
||||
unsigned int wait_time, unsigned int status_reg,
|
||||
unsigned long status_fail_mask)
|
||||
{
|
||||
unsigned long time;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_write(st->regmap, LTC2983_EEPROM_KEY_REG,
|
||||
&st->eeprom_key, sizeof(st->eeprom_key));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reinit_completion(&st->completion);
|
||||
|
||||
ret = regmap_write(st->regmap, LTC2983_STATUS_REG,
|
||||
LTC2983_STATUS_START(true) | cmd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
time = wait_for_completion_timeout(&st->completion,
|
||||
msecs_to_jiffies(wait_time));
|
||||
if (!time) {
|
||||
dev_err(&st->spi->dev, "EEPROM command timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
ret = regmap_read(st->regmap, status_reg, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val & status_fail_mask) {
|
||||
dev_err(&st->spi->dev, "EEPROM command failed: 0x%02X\n", val);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ltc2983_setup(struct ltc2983_data *st, bool assign_iio)
|
||||
{
|
||||
u32 iio_chan_t = 0, iio_chan_v = 0, chan, iio_idx = 0, status;
|
||||
|
@ -1396,6 +1515,15 @@ static int ltc2983_setup(struct ltc2983_data *st, bool assign_iio)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (st->info->has_eeprom && !assign_iio) {
|
||||
ret = ltc2983_eeprom_cmd(st, LTC2983_EEPROM_READ_CMD,
|
||||
LTC2983_EEPROM_READ_TIME_MS,
|
||||
LTC2983_EEPROM_READ_STATUS_REG,
|
||||
LTC2983_EEPROM_READ_FAILURE_MASK);
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (chan = 0; chan < st->num_channels; chan++) {
|
||||
u32 chan_type = 0, *iio_chan;
|
||||
|
||||
|
@ -1435,9 +1563,13 @@ static int ltc2983_setup(struct ltc2983_data *st, bool assign_iio)
|
|||
static const struct regmap_range ltc2983_reg_ranges[] = {
|
||||
regmap_reg_range(LTC2983_STATUS_REG, LTC2983_STATUS_REG),
|
||||
regmap_reg_range(LTC2983_TEMP_RES_START_REG, LTC2983_TEMP_RES_END_REG),
|
||||
regmap_reg_range(LTC2983_EEPROM_KEY_REG, LTC2983_EEPROM_KEY_REG),
|
||||
regmap_reg_range(LTC2983_EEPROM_READ_STATUS_REG,
|
||||
LTC2983_EEPROM_READ_STATUS_REG),
|
||||
regmap_reg_range(LTC2983_GLOBAL_CONFIG_REG, LTC2983_GLOBAL_CONFIG_REG),
|
||||
regmap_reg_range(LTC2983_MULT_CHANNEL_START_REG,
|
||||
LTC2983_MULT_CHANNEL_END_REG),
|
||||
regmap_reg_range(LTC2986_EEPROM_STATUS_REG, LTC2986_EEPROM_STATUS_REG),
|
||||
regmap_reg_range(LTC2983_MUX_CONFIG_REG, LTC2983_MUX_CONFIG_REG),
|
||||
regmap_reg_range(LTC2983_CHAN_ASSIGN_START_REG,
|
||||
LTC2983_CHAN_ASSIGN_END_REG),
|
||||
|
@ -1482,6 +1614,12 @@ static int ltc2983_probe(struct spi_device *spi)
|
|||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
st->info = device_get_match_data(&spi->dev);
|
||||
if (!st->info)
|
||||
st->info = (void *)spi_get_device_id(spi)->driver_data;
|
||||
if (!st->info)
|
||||
return -ENODEV;
|
||||
|
||||
st->regmap = devm_regmap_init_spi(spi, <c2983_regmap_config);
|
||||
if (IS_ERR(st->regmap)) {
|
||||
dev_err(&spi->dev, "Failed to initialize regmap\n");
|
||||
|
@ -1491,6 +1629,7 @@ static int ltc2983_probe(struct spi_device *spi)
|
|||
mutex_init(&st->lock);
|
||||
init_completion(&st->completion);
|
||||
st->spi = spi;
|
||||
st->eeprom_key = cpu_to_be32(LTC2983_EEPROM_KEY);
|
||||
spi_set_drvdata(spi, st);
|
||||
|
||||
ret = ltc2983_parse_dt(st);
|
||||
|
@ -1524,6 +1663,15 @@ static int ltc2983_probe(struct spi_device *spi)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (st->info->has_eeprom) {
|
||||
ret = ltc2983_eeprom_cmd(st, LTC2983_EEPROM_WRITE_CMD,
|
||||
LTC2983_EEPROM_WRITE_TIME_MS,
|
||||
LTC2986_EEPROM_STATUS_REG,
|
||||
LTC2983_EEPROM_STATUS_FAILURE_MASK);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev->name = name;
|
||||
indio_dev->num_channels = st->iio_channels;
|
||||
indio_dev->channels = st->iio_chan;
|
||||
|
@ -1554,14 +1702,35 @@ static int ltc2983_suspend(struct device *dev)
|
|||
static DEFINE_SIMPLE_DEV_PM_OPS(ltc2983_pm_ops, ltc2983_suspend,
|
||||
ltc2983_resume);
|
||||
|
||||
static const struct ltc2983_chip_info ltc2983_chip_info_data = {
|
||||
.max_channels_nr = 20,
|
||||
};
|
||||
|
||||
static const struct ltc2983_chip_info ltc2984_chip_info_data = {
|
||||
.max_channels_nr = 20,
|
||||
.has_eeprom = true,
|
||||
};
|
||||
|
||||
static const struct ltc2983_chip_info ltc2986_chip_info_data = {
|
||||
.max_channels_nr = 10,
|
||||
.has_temp = true,
|
||||
.has_eeprom = true,
|
||||
};
|
||||
|
||||
static const struct spi_device_id ltc2983_id_table[] = {
|
||||
{ "ltc2983" },
|
||||
{ "ltc2983", (kernel_ulong_t)<c2983_chip_info_data },
|
||||
{ "ltc2984", (kernel_ulong_t)<c2984_chip_info_data },
|
||||
{ "ltc2986", (kernel_ulong_t)<c2986_chip_info_data },
|
||||
{ "ltm2985", (kernel_ulong_t)<c2986_chip_info_data },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ltc2983_id_table);
|
||||
|
||||
static const struct of_device_id ltc2983_of_match[] = {
|
||||
{ .compatible = "adi,ltc2983" },
|
||||
{ .compatible = "adi,ltc2983", .data = <c2983_chip_info_data },
|
||||
{ .compatible = "adi,ltc2984", .data = <c2984_chip_info_data },
|
||||
{ .compatible = "adi,ltc2986", .data = <c2986_chip_info_data },
|
||||
{ .compatible = "adi,ltm2985", .data = <c2986_chip_info_data },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ltc2983_of_match);
|
||||
|
|
Загрузка…
Ссылка в новой задаче