Commit 6f7cadcf authored by Cosmin Tanislav's avatar Cosmin Tanislav Committed by Jonathan Cameron
Browse files

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: default avatarCosmin Tanislav <cosmin.tanislav@analog.com>
Link: https://lore.kernel.org/r/20221103130041.2153295-14-demonsingur@gmail.com


Signed-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent d2405269
Loading
Loading
Loading
Loading
+176 −7
Original line number Diff line number Diff line
@@ -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 @@ static int ltc2983_parse_dt(struct ltc2983_data *st)
	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, &ltc2983_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)&ltc2983_chip_info_data },
	{ "ltc2984", (kernel_ulong_t)&ltc2984_chip_info_data },
	{ "ltc2986", (kernel_ulong_t)&ltc2986_chip_info_data },
	{ "ltm2985", (kernel_ulong_t)&ltc2986_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 = &ltc2983_chip_info_data },
	{ .compatible = "adi,ltc2984", .data = &ltc2984_chip_info_data },
	{ .compatible = "adi,ltc2986", .data = &ltc2986_chip_info_data },
	{ .compatible = "adi,ltm2985", .data = &ltc2986_chip_info_data },
	{},
};
MODULE_DEVICE_TABLE(of, ltc2983_of_match);