All of lore.kernel.org
 help / color / mirror / Atom feed
From: Yusuf Alper Bilgin <y.alperbilgin@gmail.com>
To: "Lars-Peter Clausen" <lars@metafoo.de>,
	"Michael Hennerich" <Michael.Hennerich@analog.com>,
	"Jonathan Cameron" <jic23@kernel.org>,
	"David Lechner" <dlechner@baylibre.com>,
	"Nuno Sá" <nuno.sa@analog.com>,
	"Andy Shevchenko" <andy@kernel.org>,
	"Rob Herring" <robh@kernel.org>,
	"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
	"Conor Dooley" <conor+dt@kernel.org>,
	"Liam Beguin" <liambeguin@gmail.com>
Cc: Michael Hennerich <michael.hennerich@analog.com>,
	 linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
	 linux-kernel@vger.kernel.org,
	Yusuf Alper Bilgin <y.alperbilgin@gmail.com>
Subject: [PATCH v2 3/4] iio: adc: ltc2497: add temperature sensor support
Date: Wed, 13 Aug 2025 18:54:31 +0200	[thread overview]
Message-ID: <20250813-ltc2495-v2-3-bbaf20f6ba07@gmail.com> (raw)
In-Reply-To: <20250813-ltc2495-v2-0-bbaf20f6ba07@gmail.com>

Support for reading the internal temperature sensor on LTC2495 and
LTC2499 via a standard IIO temperature channel.

Signed-off-by: Yusuf Alper Bilgin <y.alperbilgin@gmail.com>
---
 drivers/iio/adc/ltc2497-core.c | 140 ++++++++++++++++++++++++++++++-----------
 drivers/iio/adc/ltc2497.c      |  28 ++++++++-
 drivers/iio/adc/ltc2497.h      |  17 ++++-
 3 files changed, 145 insertions(+), 40 deletions(-)

diff --git a/drivers/iio/adc/ltc2497-core.c b/drivers/iio/adc/ltc2497-core.c
index 2dc5c704426949a4ec62c42591d6c2c40ffb79cc..0e21d5f59e6108be8b5d79bd706425278043b44d 100644
--- a/drivers/iio/adc/ltc2497-core.c
+++ b/drivers/iio/adc/ltc2497-core.c
@@ -18,6 +18,7 @@
 #define LTC2497_SGL			BIT(4)
 #define LTC2497_DIFF			0
 #define LTC2497_SIGN			BIT(3)
+#define LTC2497_KELVIN_TO_CELCIUS	273
 
 static int ltc2497core_wait_conv(struct ltc2497core_driverdata *ddata)
 {
@@ -95,10 +96,53 @@ static int ltc2497core_read_raw(struct iio_dev *indio_dev,
 		if (ret < 0)
 			return ret;
 
-		*val = ret / 1000;
-		*val2 = ddata->chip_info->resolution + 1;
+		switch (chan->type) {
+		case IIO_VOLTAGE:
+			*val = ret / 1000;
+			*val2 = ddata->chip_info->resolution + 1;
+
+			return IIO_VAL_FRACTIONAL_LOG2;
+
+		case IIO_TEMP:
+			if (!ddata->chip_info->has_temp_channel)
+				return -EINVAL;
+
+			/*
+			 * The datasheet formula to get Temperature in Celsius is:
+			 * Temp_C = (DATAOUT * Vref_V / temp_scale) - 273
+			 *
+			 * To match the IIO framework's model of (raw + offset) * scale,
+			 * and to get the final result in millidegrees Celsius:
+			 *
+			 * Temp_mC = ((DATAOUT * Vref_mV / temp_scale_mV) - 273) * 1000
+			 * Temp_mC = (DATAOUT - (273 * temp_scale_mV / Vref_mv)) * 1000 *
+			 *           Vref_mV / temp_scale_mV
+			 *
+			 * This gives us:
+			 * scale  = Vref_mV * 1000 / temp_scale_mV
+			 * offset = -273 * temp_scale / Vref_mV
+			 */
+			*val = ret;
+			*val2 = ddata->chip_info->temp_scale_mV;
+
+			return IIO_VAL_FRACTIONAL;
+
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_OFFSET:
+		if (chan->type != IIO_TEMP)
+			return -EINVAL;
+
+		/* see the calculation above. Offset with (-273 * temp_scale / Vref) */
+		ret = regulator_get_voltage(ddata->ref);
+		if (ret < 0)
+			return ret;
 
-		return IIO_VAL_FRACTIONAL_LOG2;
+		*val = -LTC2497_KELVIN_TO_CELCIUS * ddata->chip_info->temp_scale_mV;
+		*val2 = ret / 1000;
+
+		return IIO_VAL_FRACTIONAL;
 
 	default:
 		return -EINVAL;
@@ -126,39 +170,55 @@ static int ltc2497core_read_raw(struct iio_dev *indio_dev,
 	.differential = 1, \
 }
 
+#define LTC2497_T_CHAN {									\
+	.type = IIO_TEMP,									\
+	.channel = 0,										\
+	.address = (LTC2497_ENABLE_TEMPERATURE_CONV),						\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),						\
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),	\
+}
+
+#define LTC2497_CHANNELS \
+	LTC2497_CHAN(0, LTC2497_SGL, "CH0"),			\
+	LTC2497_CHAN(1, LTC2497_SGL, "CH1"),			\
+	LTC2497_CHAN(2, LTC2497_SGL, "CH2"),			\
+	LTC2497_CHAN(3, LTC2497_SGL, "CH3"),			\
+	LTC2497_CHAN(4, LTC2497_SGL, "CH4"),			\
+	LTC2497_CHAN(5, LTC2497_SGL, "CH5"),			\
+	LTC2497_CHAN(6, LTC2497_SGL, "CH6"),			\
+	LTC2497_CHAN(7, LTC2497_SGL, "CH7"),			\
+	LTC2497_CHAN(8, LTC2497_SGL, "CH8"),			\
+	LTC2497_CHAN(9, LTC2497_SGL, "CH9"),			\
+	LTC2497_CHAN(10, LTC2497_SGL, "CH10"),			\
+	LTC2497_CHAN(11, LTC2497_SGL, "CH11"),			\
+	LTC2497_CHAN(12, LTC2497_SGL, "CH12"),			\
+	LTC2497_CHAN(13, LTC2497_SGL, "CH13"),			\
+	LTC2497_CHAN(14, LTC2497_SGL, "CH14"),			\
+	LTC2497_CHAN(15, LTC2497_SGL, "CH15"),			\
+	LTC2497_CHAN_DIFF(0, LTC2497_DIFF),			\
+	LTC2497_CHAN_DIFF(1, LTC2497_DIFF),			\
+	LTC2497_CHAN_DIFF(2, LTC2497_DIFF),			\
+	LTC2497_CHAN_DIFF(3, LTC2497_DIFF),			\
+	LTC2497_CHAN_DIFF(4, LTC2497_DIFF),			\
+	LTC2497_CHAN_DIFF(5, LTC2497_DIFF),			\
+	LTC2497_CHAN_DIFF(6, LTC2497_DIFF),			\
+	LTC2497_CHAN_DIFF(7, LTC2497_DIFF),			\
+	LTC2497_CHAN_DIFF(0, LTC2497_DIFF | LTC2497_SIGN),	\
+	LTC2497_CHAN_DIFF(1, LTC2497_DIFF | LTC2497_SIGN),	\
+	LTC2497_CHAN_DIFF(2, LTC2497_DIFF | LTC2497_SIGN),	\
+	LTC2497_CHAN_DIFF(3, LTC2497_DIFF | LTC2497_SIGN),	\
+	LTC2497_CHAN_DIFF(4, LTC2497_DIFF | LTC2497_SIGN),	\
+	LTC2497_CHAN_DIFF(5, LTC2497_DIFF | LTC2497_SIGN),	\
+	LTC2497_CHAN_DIFF(6, LTC2497_DIFF | LTC2497_SIGN),	\
+	LTC2497_CHAN_DIFF(7, LTC2497_DIFF | LTC2497_SIGN)
+
 static const struct iio_chan_spec ltc2497core_channel[] = {
-	LTC2497_CHAN(0, LTC2497_SGL, "CH0"),
-	LTC2497_CHAN(1, LTC2497_SGL, "CH1"),
-	LTC2497_CHAN(2, LTC2497_SGL, "CH2"),
-	LTC2497_CHAN(3, LTC2497_SGL, "CH3"),
-	LTC2497_CHAN(4, LTC2497_SGL, "CH4"),
-	LTC2497_CHAN(5, LTC2497_SGL, "CH5"),
-	LTC2497_CHAN(6, LTC2497_SGL, "CH6"),
-	LTC2497_CHAN(7, LTC2497_SGL, "CH7"),
-	LTC2497_CHAN(8, LTC2497_SGL, "CH8"),
-	LTC2497_CHAN(9, LTC2497_SGL, "CH9"),
-	LTC2497_CHAN(10, LTC2497_SGL, "CH10"),
-	LTC2497_CHAN(11, LTC2497_SGL, "CH11"),
-	LTC2497_CHAN(12, LTC2497_SGL, "CH12"),
-	LTC2497_CHAN(13, LTC2497_SGL, "CH13"),
-	LTC2497_CHAN(14, LTC2497_SGL, "CH14"),
-	LTC2497_CHAN(15, LTC2497_SGL, "CH15"),
-	LTC2497_CHAN_DIFF(0, LTC2497_DIFF),
-	LTC2497_CHAN_DIFF(1, LTC2497_DIFF),
-	LTC2497_CHAN_DIFF(2, LTC2497_DIFF),
-	LTC2497_CHAN_DIFF(3, LTC2497_DIFF),
-	LTC2497_CHAN_DIFF(4, LTC2497_DIFF),
-	LTC2497_CHAN_DIFF(5, LTC2497_DIFF),
-	LTC2497_CHAN_DIFF(6, LTC2497_DIFF),
-	LTC2497_CHAN_DIFF(7, LTC2497_DIFF),
-	LTC2497_CHAN_DIFF(0, LTC2497_DIFF | LTC2497_SIGN),
-	LTC2497_CHAN_DIFF(1, LTC2497_DIFF | LTC2497_SIGN),
-	LTC2497_CHAN_DIFF(2, LTC2497_DIFF | LTC2497_SIGN),
-	LTC2497_CHAN_DIFF(3, LTC2497_DIFF | LTC2497_SIGN),
-	LTC2497_CHAN_DIFF(4, LTC2497_DIFF | LTC2497_SIGN),
-	LTC2497_CHAN_DIFF(5, LTC2497_DIFF | LTC2497_SIGN),
-	LTC2497_CHAN_DIFF(6, LTC2497_DIFF | LTC2497_SIGN),
-	LTC2497_CHAN_DIFF(7, LTC2497_DIFF | LTC2497_SIGN),
+	LTC2497_CHANNELS,
+};
+
+static const struct iio_chan_spec ltc2497core_channel_with_temperature[] = {
+	LTC2497_CHANNELS,
+	LTC2497_T_CHAN,
 };
 
 static const struct iio_info ltc2497core_info = {
@@ -182,8 +242,14 @@ int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev)
 
 	indio_dev->info = &ltc2497core_info;
 	indio_dev->modes = INDIO_DIRECT_MODE;
-	indio_dev->channels = ltc2497core_channel;
-	indio_dev->num_channels = ARRAY_SIZE(ltc2497core_channel);
+
+	if (ddata->chip_info->has_temp_channel) {
+		indio_dev->channels = ltc2497core_channel_with_temperature;
+		indio_dev->num_channels = ARRAY_SIZE(ltc2497core_channel_with_temperature);
+	} else {
+		indio_dev->channels = ltc2497core_channel;
+		indio_dev->num_channels = ARRAY_SIZE(ltc2497core_channel);
+	}
 
 	ret = ddata->result_and_measure(ddata, LTC2497_CONFIG_DEFAULT, NULL);
 	if (ret < 0)
diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c
index 8f4665547b5b0d32084599f8557c40102c37a4ce..07fced79aeead3778964b114d479fdcb643c16df 100644
--- a/drivers/iio/adc/ltc2497.c
+++ b/drivers/iio/adc/ltc2497.c
@@ -86,8 +86,28 @@ static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata,
 			return 0;
 	}
 
-	ret = i2c_smbus_write_byte(st->client,
-				   LTC2497_ENABLE | address);
+	/*
+	 * Chips with temperature sensor support (e.g., LTC2495/LTC2499)
+	 * require a two-byte command format to select any channel.
+	 *
+	 * To read the internal temperature, LTC2497_ENABLE_TEMPERATURE_CONV
+	 * is sent as the second byte. To read a voltage channel, LTC2497_EN2
+	 * is sent, which sets the default configuration: simultaneous 50/60Hz
+	 * rejection, 1x speed, and gain=1.
+	 *
+	 * Chips without this feature use a standard single-byte command.
+	 */
+	if (ddata->chip_info->has_temp_channel) {
+		if (address == LTC2497_ENABLE_TEMPERATURE_CONV)
+			ret = i2c_smbus_write_byte_data(st->client, LTC2497_ENABLE,
+							LTC2497_ENABLE_TEMPERATURE_CONV);
+		else
+			ret = i2c_smbus_write_byte_data(st->client, LTC2497_ENABLE | address,
+							LTC2497_EN2);
+	} else {
+		ret = i2c_smbus_write_byte(st->client, LTC2497_ENABLE | address);
+	}
+
 	if (ret)
 		dev_err(&st->client->dev, "i2c transfer failed: %pe\n",
 			ERR_PTR(ret));
@@ -135,6 +155,8 @@ static const struct ltc2497_chip_info ltc2497_info[] = {
 	[TYPE_LTC2495] = {
 		.resolution = 16,
 		.name = "ltc2495",
+		.has_temp_channel = true,
+		.temp_scale_mV = 12250,
 	},
 	[TYPE_LTC2497] = {
 		.resolution = 16,
@@ -143,6 +165,8 @@ static const struct ltc2497_chip_info ltc2497_info[] = {
 	[TYPE_LTC2499] = {
 		.resolution = 24,
 		.name = "ltc2499",
+		.has_temp_channel = true,
+		.temp_scale_mV = 1570000,
 	},
 };
 
diff --git a/drivers/iio/adc/ltc2497.h b/drivers/iio/adc/ltc2497.h
index 64e81c95a3dd05911b6717c09ac0560c9f47f304..7f53bf8b8469d2371bb7ae64ce90a61beadba9ba 100644
--- a/drivers/iio/adc/ltc2497.h
+++ b/drivers/iio/adc/ltc2497.h
@@ -3,10 +3,25 @@
 #define LTC2497_ENABLE			0xA0
 #define LTC2497_CONFIG_DEFAULT		LTC2497_ENABLE
 #define LTC2497_CONVERSION_TIME_MS	150ULL
+#define LTC2497_EN2			BIT(7)
+/* Enable the internal temperature sensor */
+#define LTC2497_IM                      BIT(6)
+/* Second command byte value to initiate a temperature conversion */
+#define LTC2497_ENABLE_TEMPERATURE_CONV	(LTC2497_EN2 | LTC2497_IM)
 
 struct ltc2497_chip_info {
-	u32 resolution;
 	const char *name;
+	u32 resolution;
+	/*
+	 * Represents the datasheet constant from the temperature formula:
+	 * T_Kelvin = (DATAOUT * Vref) / temp_scale, where Vref is in Volts.
+	 *
+	 * To allow the driver to use Vref in millivolts for the calculation
+	 * and also to avoid floating points, this stored value represents the
+	 * datasheet constant scaled by 1000.
+	 */
+	u32 temp_scale_mV;
+	bool has_temp_channel;
 };
 
 struct ltc2497core_driverdata {

-- 
2.43.0


  parent reply	other threads:[~2025-08-13 16:54 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-08-13 16:54 [PATCH v2 0/4] Add LTC2495 support Yusuf Alper Bilgin
2025-08-13 16:54 ` [PATCH v2 1/4] dt-bindings: iio: adc: ltc2497: add lltc,ltc2495 bindings Yusuf Alper Bilgin
2025-08-13 16:54 ` [PATCH v2 2/4] iio: adc: ltc2497: add support for LTC2495 Yusuf Alper Bilgin
2025-08-13 16:54 ` Yusuf Alper Bilgin [this message]
2025-08-13 17:14   ` [PATCH v2 3/4] iio: adc: ltc2497: add temperature sensor support Andy Shevchenko
2025-08-13 16:54 ` [PATCH v2 4/4] iio: adc: ltc2497: reorder ltc2497core_driverdata members to remove hole Yusuf Alper Bilgin
2025-08-13 17:16   ` Andy Shevchenko
2025-08-13 17:17 ` [PATCH v2 0/4] Add LTC2495 support Andy Shevchenko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250813-ltc2495-v2-3-bbaf20f6ba07@gmail.com \
    --to=y.alperbilgin@gmail.com \
    --cc=Michael.Hennerich@analog.com \
    --cc=andy@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dlechner@baylibre.com \
    --cc=jic23@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=lars@metafoo.de \
    --cc=liambeguin@gmail.com \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=nuno.sa@analog.com \
    --cc=robh@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.