-
Notifications
You must be signed in to change notification settings - Fork 8k
Enable temperature sensor on rt700 #95630
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
36ac574
f4c8e52
7fb85b2
e57e5fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Copyright 2025 NXP | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
zephyr_library() | ||
zephyr_library_sources(nxp_pmc_tmpsns.c) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Copyright 2025 NXP | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
config NXP_PMC_TMPSNS | ||
bool "NXP PMC Temperature Sensor (TMPSNS)" | ||
default y | ||
depends on DT_HAS_NXP_PMC_TMPSNS_ENABLED | ||
select FPU if CPU_HAS_FPU | ||
help | ||
Enable driver for the NXP PMC temperature sensor. | ||
This is used to retrieve on-die operational temperature. | ||
|
||
if NXP_PMC_TMPSNS | ||
config NXP_PMC_TMPSNS_CALIBRATION_OTP_FUSE_INDEX | ||
int "OTP FUSE index" | ||
default 77 if SOC_SERIES_IMXRT7XX | ||
help | ||
TSENS_CAL is an 8-bit signed calibration constant | ||
retrieved from non-volatile memory. We need this | ||
index to read the fuse to get TSENS_CAL. | ||
endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
/* | ||
* Copyright 2025 NXP | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include "fsl_romapi_otp.h" | ||
#include <zephyr/device.h> | ||
#include <zephyr/devicetree.h> | ||
#include <zephyr/drivers/sensor.h> | ||
#include <zephyr/drivers/adc.h> | ||
#include <zephyr/logging/log.h> | ||
|
||
LOG_MODULE_REGISTER(nxp_pmc_tmpsns, CONFIG_SENSOR_LOG_LEVEL); | ||
|
||
#define DT_DRV_COMPAT nxp_pmc_tmpsns | ||
|
||
struct nxp_pmc_tmpsns_config { | ||
const struct device *adc; | ||
struct adc_sequence adc_seq; | ||
struct adc_channel_cfg ch_cfg; | ||
}; | ||
|
||
struct nxp_pmc_tmpsns_data { | ||
uint16_t buffer; | ||
uint32_t pmc_tmpsns_calibration; | ||
float pmc_tmpsns_value; | ||
}; | ||
|
||
static int nxp_pmc_tmpsns_sample_fetch(const struct device *dev, enum sensor_channel chan) | ||
{ | ||
uint8_t pmc_tmpsns_select[15] = {0, 1, 3, 2, 6, 7, 5, 4, 5, 7, 6, 2, 3, 1, 0}; | ||
const struct nxp_pmc_tmpsns_config *config = dev->config; | ||
struct nxp_pmc_tmpsns_data *data = dev->data; | ||
uint16_t pmc_tmpsns_value[15] = {0}; | ||
float cm_vref, cm_ctat, cm_temp; | ||
int8_t calibration = 0; | ||
int ret; | ||
|
||
if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_DIE_TEMP) { | ||
return -ENOTSUP; | ||
} | ||
|
||
for (uint8_t index = 0; index < sizeof(pmc_tmpsns_select); ++index) { | ||
PMC0->TSENSOR = PMC_TSENSOR_TSENSM(pmc_tmpsns_select[index]); | ||
|
||
ret = adc_read(config->adc, &config->adc_seq); | ||
if (ret) { | ||
LOG_ERR("Failed to read ADC channels with code %d", ret); | ||
return ret; | ||
} | ||
pmc_tmpsns_value[index] = data->buffer; | ||
} | ||
|
||
cm_ctat = (float)(2 * pmc_tmpsns_value[1] - pmc_tmpsns_value[2] + | ||
2 * pmc_tmpsns_value[13] - pmc_tmpsns_value[12] + | ||
2 * pmc_tmpsns_value[6] - pmc_tmpsns_value[5] + | ||
2 * pmc_tmpsns_value[8] - pmc_tmpsns_value[9]) / 4.0f; | ||
|
||
cm_temp = (float)(2 * pmc_tmpsns_value[0] - pmc_tmpsns_value[3] + | ||
2 * pmc_tmpsns_value[14] - pmc_tmpsns_value[11] + | ||
4 * pmc_tmpsns_value[7] - pmc_tmpsns_value[4] - | ||
pmc_tmpsns_value[10]) / 4.0f; | ||
|
||
calibration = (int8_t)(data->pmc_tmpsns_calibration & 0xFF); | ||
|
||
cm_vref = cm_ctat + (953.36f + calibration) * cm_temp / 2048; | ||
|
||
data->pmc_tmpsns_value = 370.98f * (cm_temp / cm_vref) - 273.15f; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would suggest to remove the float calculation above and change to int32 instead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to know your starting point of the suggestion, actually this driver will only be used by RT500, RT600, and RT700, which all have FPU, so I think it is necessary to maintain accuracy as much as possible during the calculation process. |
||
|
||
return 0; | ||
} | ||
|
||
static int nxp_pmc_tmpsns_channel_get(const struct device *dev, | ||
enum sensor_channel chan, | ||
struct sensor_value *val) | ||
{ | ||
const struct nxp_pmc_tmpsns_data *data = dev->data; | ||
|
||
if (chan != SENSOR_CHAN_DIE_TEMP) { | ||
return -ENOTSUP; | ||
} | ||
|
||
return sensor_value_from_float(val, data->pmc_tmpsns_value); | ||
} | ||
|
||
static int nxp_pmc_tmpsns_init(const struct device *dev) | ||
{ | ||
const struct nxp_pmc_tmpsns_config *config = dev->config; | ||
struct nxp_pmc_tmpsns_data *data = dev->data; | ||
int ret; | ||
|
||
if (!device_is_ready(config->adc)) { | ||
LOG_ERR("ADC device not ready"); | ||
return -ENODEV; | ||
} | ||
|
||
ret = adc_channel_setup(config->adc, &config->ch_cfg); | ||
if (ret) { | ||
LOG_ERR("Failed to setup ADC channel with code %d", ret); | ||
return ret; | ||
} | ||
|
||
ret = otp_fuse_read(CONFIG_NXP_PMC_TMPSNS_CALIBRATION_OTP_FUSE_INDEX, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you help me understand where is this function defined and declared? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function is defined in fsl_romapi_opt.h. This PR requires romapi_opt update to the latest, so there is a hal_nxp PR There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. okay. I was considering in the past making a syscon driver (drivers/syscon) for OCOTP. I think in the meantime someone already added an example of a fuse-controller driver under syscon API. Might want to investigate at some point if this is a good idea for our OTP controllers. At least to implement reading from fuses, I dont think we want to mess with an API for writing to fuses, that wouldnt have a lot of value and would probably just screw with people. |
||
&data->pmc_tmpsns_calibration); | ||
if (ret) { | ||
LOG_ERR("Failed to get calibration value form FUSE."); | ||
return -ENOTSUP; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static DEVICE_API(sensor, nxp_pmc_tmpsns_api) = { | ||
.sample_fetch = nxp_pmc_tmpsns_sample_fetch, | ||
.channel_get = nxp_pmc_tmpsns_channel_get, | ||
}; | ||
|
||
#define NXP_PMC_TMPSNS_INIT(inst) \ | ||
static struct nxp_pmc_tmpsns_data _CONCAT(nxp_pmc_tmpsns_data, inst); \ | ||
\ | ||
static const struct nxp_pmc_tmpsns_config _CONCAT(nxp_pmc_tmpsns_config, inst) = { \ | ||
.adc = DEVICE_DT_GET(DT_INST_IO_CHANNELS_CTLR(inst)), \ | ||
.adc_seq = { \ | ||
.channels = BIT(DT_INST_IO_CHANNELS_INPUT(inst)), \ | ||
.buffer = &_CONCAT(nxp_pmc_tmpsns_data, inst).buffer, \ | ||
.buffer_size = sizeof(_CONCAT(nxp_pmc_tmpsns_data, inst)), \ | ||
.resolution = 16, \ | ||
.oversampling = 7, \ | ||
}, \ | ||
.ch_cfg = ADC_CHANNEL_CFG_DT(DT_CHILD(DT_INST_IO_CHANNELS_CTLR(inst), \ | ||
UTIL_CAT(channel_, DT_INST_IO_CHANNELS_INPUT(inst)))), \ | ||
}; \ | ||
\ | ||
SENSOR_DEVICE_DT_INST_DEFINE(inst, nxp_pmc_tmpsns_init, NULL, \ | ||
&_CONCAT(nxp_pmc_tmpsns_data, inst), \ | ||
&_CONCAT(nxp_pmc_tmpsns_config, inst), \ | ||
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ | ||
&nxp_pmc_tmpsns_api); | ||
|
||
DT_INST_FOREACH_STATUS_OKAY(NXP_PMC_TMPSNS_INIT) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Copyright NXP 2025 | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
description: NXP PMC temperature sensor (PMC-TMPSNS) | ||
|
||
compatible: "nxp,pmc-tmpsns" | ||
|
||
include: sensor-device.yaml | ||
|
||
properties: | ||
io-channels: | ||
required: true | ||
description: | | ||
This should point to an ADC channel (e.g., <&adc0 0>) | ||
to read from the PMC internal temperature sensor. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* | ||
* Copyright 2025 NXP | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include <zephyr/dt-bindings/adc/adc.h> | ||
#include <zephyr/dt-bindings/adc/mcux-lpadc.h> | ||
|
||
&pmc_tmpsns { | ||
status = "okay"; | ||
io-channels = <&lpadc0 0>; | ||
}; | ||
|
||
&lpadc0 { | ||
#address-cells = <1>; | ||
#size-cells = <0>; | ||
|
||
channel@0 { | ||
reg = <0>; | ||
zephyr,gain = "ADC_GAIN_1"; | ||
zephyr,reference = "ADC_REF_EXTERNAL0"; | ||
zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_TICKS, 131)>; | ||
zephyr,input-positive = <MCUX_LPADC_CH6A>; | ||
zephyr,input-negative = <MCUX_LPADC_CH6B>; | ||
zephyr,differential; | ||
}; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you provide a datasheet reference that specifies the calibration equation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello @MaureenHelm, In RT500 reference manual page 493, the equation is


RT700 is the same, but the reference manual is not available in NXP website