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 3/3] iio: adc: ltc2497: add temperature sensor support
Date: Tue, 12 Aug 2025 19:04:12 +0200 [thread overview]
Message-ID: <20250812-ltc2495-v1-3-7bf4c6feec2e@gmail.com> (raw)
In-Reply-To: <20250812-ltc2495-v1-0-7bf4c6feec2e@gmail.com>
The LTC2495 and LTC2499 include an internal temperature sensor. This
patch adds support for reading it via a standard IIO temperature
channel.
Signed-off-by: Yusuf Alper Bilgin <y.alperbilgin@gmail.com>
---
drivers/iio/adc/ltc2496.c | 1 +
drivers/iio/adc/ltc2497-core.c | 80 ++++++++++++++++++++++++++++++++++++++----
drivers/iio/adc/ltc2497.c | 33 ++++++++++++++---
drivers/iio/adc/ltc2497.h | 7 +++-
4 files changed, 109 insertions(+), 12 deletions(-)
diff --git a/drivers/iio/adc/ltc2496.c b/drivers/iio/adc/ltc2496.c
index f06dd0b9a85819935abea6496abe2a808ac549c6..909dbd5f9aee877aafe423b47581ff4f36a76c52 100644
--- a/drivers/iio/adc/ltc2496.c
+++ b/drivers/iio/adc/ltc2496.c
@@ -90,6 +90,7 @@ static void ltc2496_remove(struct spi_device *spi)
static const struct ltc2497_chip_info ltc2496_info = {
.resolution = 16,
.name = NULL,
+ .has_temp_channel = false,
};
static const struct of_device_id ltc2496_of_match[] = {
diff --git a/drivers/iio/adc/ltc2497-core.c b/drivers/iio/adc/ltc2497-core.c
index 400f4fe5af30e8e16b75506726235f10f2a4237f..938259df4d27e9e26dea2be4112bef355adaaafb 100644
--- a/drivers/iio/adc/ltc2497-core.c
+++ b/drivers/iio/adc/ltc2497-core.c
@@ -72,8 +72,8 @@ static int ltc2497core_read(struct ltc2497core_driverdata *ddata, u8 address, in
}
static int ltc2497core_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2, long mask)
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
{
struct ltc2497core_driverdata *ddata = iio_priv(indio_dev);
int ret;
@@ -93,10 +93,52 @@ 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 = (Conversion * Vref / temp_scale) - 273
+ *
+ * To match the IIO framework's model of (raw + offset) * scale,
+ * and to get the final result in millidegrees Celsius:
+ *
+ * = ((Conversion * Vref / temp_scale) - 273) * 1000
+ * = (Conversion - (273 * temp_scale / Vref)) * 1000 * Vref / temp_scale
+ *
+ * This gives us if the Vref is in mV:
+ * scale = Vref * 1000 / temp_scale
+ * offset = -273 * temp_scale / Vref
+ */
+ *val = ret;
+ *val2 = ddata->chip_info->temp_scale;
+
+ 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 = -273 * ddata->chip_info->temp_scale;
+ *val2 = ret / 1000;
+
+ return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
@@ -124,6 +166,14 @@ static int ltc2497core_read_raw(struct iio_dev *indio_dev,
.differential = 1, \
}
+#define LTC2497_T_CHAN() { \
+ .type = IIO_TEMP, \
+ .channel = 0, \
+ .address = (LTC2497_TEMP_CMD_ADDR), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), \
+}
+
static const struct iio_chan_spec ltc2497core_channel[] = {
LTC2497_CHAN(0, LTC2497_SGL, "CH0"),
LTC2497_CHAN(1, LTC2497_SGL, "CH1"),
@@ -159,6 +209,8 @@ static const struct iio_chan_spec ltc2497core_channel[] = {
LTC2497_CHAN_DIFF(7, LTC2497_DIFF | LTC2497_SIGN),
};
+static const struct iio_chan_spec ltc2497_temp_channel = LTC2497_T_CHAN();
+
static const struct iio_info ltc2497core_info = {
.read_raw = ltc2497core_read_raw,
};
@@ -180,8 +232,22 @@ int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev)
indio_dev->info = <c2497core_info;
indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = ltc2497core_channel;
- indio_dev->num_channels = ARRAY_SIZE(ltc2497core_channel);
+
+ /* Dynamically create the channel list */
+ if (ddata->chip_info->has_temp_channel) {
+ /* Allocate space for common channels + 1 temp channel */
+ indio_dev->num_channels = ARRAY_SIZE(ltc2497core_channel) + 1;
+ indio_dev->channels = devm_kmemdup(dev, ltc2497core_channel,
+ sizeof(ltc2497core_channel), GFP_KERNEL);
+ if (!indio_dev->channels)
+ return -ENOMEM;
+
+ memcpy((void *)&indio_dev->channels[ARRAY_SIZE(ltc2497core_channel)],
+ <c2497_temp_channel, sizeof(ltc2497_temp_channel));
+ } 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..3f8515ef465d3fda7572d63ef3a265dec89079ea 100644
--- a/drivers/iio/adc/ltc2497.c
+++ b/drivers/iio/adc/ltc2497.c
@@ -86,11 +86,31 @@ 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_TEMP_CMD_ADDR 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_TEMP_CMD_ADDR)
+ ret = i2c_smbus_write_byte_data(st->client, LTC2497_ENABLE,
+ LTC2497_TEMP_CMD_ADDR);
+ 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));
+ dev_err(&st->client->dev, "i2c transfer failed: %pe\n", ERR_PTR(ret));
return ret;
}
@@ -135,14 +155,19 @@ static const struct ltc2497_chip_info ltc2497_info[] = {
[TYPE_LTC2495] = {
.resolution = 16,
.name = "ltc2495",
+ .has_temp_channel = true,
+ .temp_scale = 12250, /* 12.250 V per degree C -> 12250 mV */
},
[TYPE_LTC2497] = {
.resolution = 16,
.name = NULL,
+ .has_temp_channel = false,
},
[TYPE_LTC2499] = {
.resolution = 24,
.name = "ltc2499",
+ .has_temp_channel = true,
+ .temp_scale = 1570000, /* 1570 V per degree C -> 1570000 mV */
},
};
diff --git a/drivers/iio/adc/ltc2497.h b/drivers/iio/adc/ltc2497.h
index f2139f260c3fe4e8772c6db9c46331de775dcd5c..c133a4fb8092b1c6a66a7336016dd361783f531c 100644
--- a/drivers/iio/adc/ltc2497.h
+++ b/drivers/iio/adc/ltc2497.h
@@ -1,10 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-#define LTC2497_ENABLE 0xA0
+#define LTC2497_ENABLE 0xA0
+#define LTC2497_EN2 0x80
+#define LTC2497_IM 0x40
+#define LTC2497_TEMP_CMD_ADDR (LTC2497_EN2 | LTC2497_IM)
struct ltc2497_chip_info {
u32 resolution;
const char *name;
+ bool has_temp_channel;
+ u32 temp_scale; /* in mV, for temp conversion */
};
struct ltc2497core_driverdata {
--
2.43.0
next prev parent reply other threads:[~2025-08-12 17:09 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-12 17:04 [PATCH 0/3] Add LTC2495 support Yusuf Alper Bilgin
2025-08-12 17:04 ` [PATCH 1/3] dt-bindings: iio: adc: ltc2497: add docs for LTC2495 Yusuf Alper Bilgin
2025-08-12 18:00 ` Krzysztof Kozlowski
2025-08-13 8:44 ` Yusuf Alper Bilgin
2025-08-16 10:23 ` Jonathan Cameron
2025-08-18 16:26 ` Alper Bilgin
2025-08-17 7:11 ` Krzysztof Kozlowski
2025-08-12 17:04 ` [PATCH 2/3] iio: adc: ltc2497: add support " Yusuf Alper Bilgin
2025-08-13 10:18 ` Andy Shevchenko
2025-08-12 17:04 ` Yusuf Alper Bilgin [this message]
2025-08-13 10:32 ` [PATCH 3/3] iio: adc: ltc2497: add temperature sensor 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=20250812-ltc2495-v1-3-7bf4c6feec2e@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.