Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions boards/nxp/mimxrt700_evk/board.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
#include "fsl_clock.h"
#include <soc.h>
#include <fsl_glikey.h>
#if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(pmc_tmpsns))
#include "fsl_romapi_otp.h"
#endif

/*!< System oscillator settling time in us */
#define SYSOSC_SETTLING_US 220U
Expand Down Expand Up @@ -544,6 +547,12 @@ void board_early_init_hook(void)
CLOCK_EnableClock(kCLOCK_Acmp0);
RESET_ClearPeripheralReset(kACMP0_RST_SHIFT_RSTn);
#endif

#if DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(pmc_tmpsns))
POWER_DisablePD(kPDRUNCFG_PD_PMC_TEMPSNS);
POWER_ApplyPD();
otp_init(SystemCoreClock);
#endif
}

static void GlikeyWriteEnable(GLIKEY_Type *base, uint8_t idx)
Expand Down
1 change: 1 addition & 0 deletions drivers/sensor/nxp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ add_subdirectory_ifdef(CONFIG_FXLS8974 fxls8974)
add_subdirectory_ifdef(CONFIG_FXOS8700 fxos8700)
add_subdirectory_ifdef(CONFIG_LPADC_TEMP40 nxp_lpadc_temp40)
add_subdirectory_ifdef(CONFIG_MCUX_LPCMP mcux_lpcmp)
add_subdirectory_ifdef(CONFIG_NXP_PMC_TMPSNS nxp_pmc_tmpsns)
add_subdirectory_ifdef(CONFIG_NXP_TEMPMON nxp_tempmon)
add_subdirectory_ifdef(CONFIG_NXP_TMPSNS nxp_tmpsns)
add_subdirectory_ifdef(CONFIG_P3T1755 p3t1755)
Expand Down
1 change: 1 addition & 0 deletions drivers/sensor/nxp/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ source "drivers/sensor/nxp/mcux_acmp/Kconfig"
source "drivers/sensor/nxp/mcux_lpcmp/Kconfig"
source "drivers/sensor/nxp/nxp_kinetis_temp/Kconfig"
source "drivers/sensor/nxp/nxp_lpadc_temp40/Kconfig"
source "drivers/sensor/nxp/nxp_pmc_tmpsns/Kconfig"
source "drivers/sensor/nxp/nxp_tempmon/Kconfig"
source "drivers/sensor/nxp/nxp_tmpsns/Kconfig"
source "drivers/sensor/nxp/p3t1755/Kconfig"
Expand Down
5 changes: 5 additions & 0 deletions drivers/sensor/nxp/nxp_pmc_tmpsns/CMakeLists.txt
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)
21 changes: 21 additions & 0 deletions drivers/sensor/nxp/nxp_pmc_tmpsns/Kconfig
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
141 changes: 141 additions & 0 deletions drivers/sensor/nxp/nxp_pmc_tmpsns/nxp_pmc_tmpsns.c
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;
Comment on lines +55 to +69
Copy link
Member

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?

Copy link
Contributor Author

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
image
RT700 is the same, but the reference manual is not available in NXP website
image

Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you help me understand where is this function defined and declared?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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

Copy link
Member

@decsny decsny Sep 19, 2025

Choose a reason for hiding this comment

The 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)
9 changes: 9 additions & 0 deletions dts/arm/nxp/nxp_rt7xx_cm33_cpu0.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
#include <zephyr/dt-bindings/misc/nxp_rtxxx_dsp_ctrl.h>

/ {
aliases {
die-temp0 = &pmc_tmpsns;
};

cpus {
#address-cells = <1>;
#size-cells = <0>;
Expand Down Expand Up @@ -1105,6 +1109,11 @@
interrupts = <17 0>;
status = "disabled";
};

pmc_tmpsns: pmc-tmpsns {
compatible = "nxp,pmc-tmpsns";
status = "disabled";
};
};

&xspi0 {
Expand Down
15 changes: 15 additions & 0 deletions dts/bindings/sensor/nxp,pmc-tmpsns.yaml
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.
3 changes: 3 additions & 0 deletions modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ if(CONFIG_SOC_SERIES_IMXRT5XX OR CONFIG_SOC_SERIES_IMXRT6XX)
endif()

if(CONFIG_SOC_SERIES_IMXRT7XX)
if(CONFIG_DT_HAS_NXP_PMC_TMPSNS_ENABLED)
set(CONFIG_MCUX_COMPONENT_driver.romapi ON)
endif()
set_variable_ifdef(CONFIG_HWINFO_MCUX_RSTCTL CONFIG_MCUX_COMPONENT_driver.reset)
endif()

Expand Down
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;
};
};
Loading