diff --git a/drivers/adc/adc_mcux_lpadc.c b/drivers/adc/adc_mcux_lpadc.c index 7804bdec94bd..35d4d81b6438 100644 --- a/drivers/adc/adc_mcux_lpadc.c +++ b/drivers/adc/adc_mcux_lpadc.c @@ -225,6 +225,16 @@ static int mcux_lpadc_channel_setup(const struct device *dev, } } else if (channel_cfg->reference == ADC_REF_EXTERNAL0) { LOG_DBG("ref external0"); +# if defined(FSL_FEATURE_LPADC_HAS_INTERNAL_TEMP_SENSOR) + } else if (channel_cfg->reference == ADC_REF_INTERNAL) { + /* + * We are assuming that if the reference has been set to + * ADC_REF_INTERNAL that the channel is being used to sample the + * internal temperature sensor. + */ + LOG_DBG("ref internal"); + cmd->loopCount = FSL_FEATURE_LPADC_TEMP_SENS_BUFFER_SIZE - 1U; +#endif } else { LOG_DBG("ref not support"); return -EINVAL; @@ -477,8 +487,12 @@ static int mcux_lpadc_init(const struct device *dev) lpadc_config_t adc_config; int err; + /* + * pinctrl_apply_state() returns -ENOENT if pinctrl is not provided. This is + * expected if only using the internal channels. + */ err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); - if (err) { + if (err && err != -ENOENT) { return err; } diff --git a/drivers/sensor/nxp/CMakeLists.txt b/drivers/sensor/nxp/CMakeLists.txt index 80ccb0447dbf..eeff278ebedc 100644 --- a/drivers/sensor/nxp/CMakeLists.txt +++ b/drivers/sensor/nxp/CMakeLists.txt @@ -13,4 +13,5 @@ add_subdirectory_ifdef(CONFIG_QDEC_NXP_S32 qdec_nxp_s32) add_subdirectory_ifdef(CONFIG_QDEC_TPM qdec_tpm) add_subdirectory_ifdef(CONFIG_SENSOR_MCUX_ACMP mcux_acmp) add_subdirectory_ifdef(CONFIG_TEMP_KINETIS nxp_kinetis_temp) +add_subdirectory_ifdef(CONFIG_TEMP_LPC nxp_lpc_temp) # zephyr-keep-sorted-stop diff --git a/drivers/sensor/nxp/Kconfig b/drivers/sensor/nxp/Kconfig index 76b58bbba7b4..252d44152381 100644 --- a/drivers/sensor/nxp/Kconfig +++ b/drivers/sensor/nxp/Kconfig @@ -8,6 +8,7 @@ source "drivers/sensor/nxp/fxos8700/Kconfig" 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_lpc_temp/Kconfig" source "drivers/sensor/nxp/nxp_tempmon/Kconfig" source "drivers/sensor/nxp/p3t1755/Kconfig" source "drivers/sensor/nxp/qdec_mcux/Kconfig" diff --git a/drivers/sensor/nxp/nxp_lpc_temp/CMakeLists.txt b/drivers/sensor/nxp/nxp_lpc_temp/CMakeLists.txt new file mode 100644 index 000000000000..cbb4d9c06e85 --- /dev/null +++ b/drivers/sensor/nxp/nxp_lpc_temp/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (c) 2025 Nova Dynamics LLC +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(temp_lpc.c) diff --git a/drivers/sensor/nxp/nxp_lpc_temp/Kconfig b/drivers/sensor/nxp/nxp_lpc_temp/Kconfig new file mode 100644 index 000000000000..fb9da8f96c0b --- /dev/null +++ b/drivers/sensor/nxp/nxp_lpc_temp/Kconfig @@ -0,0 +1,9 @@ +# Copyright (c) 2025 Nova Dynamics LLC +# SPDX-License-Identifier: Apache-2.0 + +config TEMP_LPC + bool "NXP LPC Temperature Sensor" + default y + depends on ADC_MCUX_LPADC && DT_HAS_NXP_LPC_TEMPERATURE_ENABLED + help + Enable driver for NXP LPC internal temperature sensor. diff --git a/drivers/sensor/nxp/nxp_lpc_temp/temp_lpc.c b/drivers/sensor/nxp/nxp_lpc_temp/temp_lpc.c new file mode 100644 index 000000000000..1c30882a2d58 --- /dev/null +++ b/drivers/sensor/nxp/nxp_lpc_temp/temp_lpc.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2025 Nova Dynamics LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_lpc_temperature + +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(temp_lpc, CONFIG_SENSOR_LOG_LEVEL); + +#include + +BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) <= 1, + "only one instance is supported"); + +struct temp_lpc_config { + const struct device *adc; + uint8_t sensor_adc_ch; + struct adc_sequence adc_seq; + const struct adc_channel_cfg adc_ch_cfg[]; +}; + +struct temp_lpc_data { + uint16_t buffer[FSL_FEATURE_LPADC_TEMP_SENS_BUFFER_SIZE]; +}; + +static struct temp_lpc_data inst_data; + +static const struct temp_lpc_config inst_config = { + .adc = DEVICE_DT_GET(DT_INST_IO_CHANNELS_CTLR(0)), + .sensor_adc_ch = DT_INST_IO_CHANNELS_INPUT_BY_IDX(0, 0), + .adc_seq = { + .options = NULL, + .channels = BIT(DT_INST_IO_CHANNELS_INPUT_BY_IDX(0, 0)), + .buffer = &inst_data.buffer, + .buffer_size = sizeof(inst_data.buffer), + .resolution = 16, + .oversampling = 7, + .calibrate = false, + }, + .adc_ch_cfg = { + DT_FOREACH_CHILD_SEP(DT_INST_IO_CHANNELS_CTLR(0), ADC_CHANNEL_CFG_DT, (,)) + }, +}; + +static int temp_lpc_sample_fetch(const struct device *dev, + enum sensor_channel chan) +{ + const struct temp_lpc_config *config = dev->config; + + if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_DIE_TEMP) { + return -ENOTSUP; + } + + return adc_read(config->adc, &config->adc_seq); +} + +static int temp_lpc_channel_get(const struct device *dev, + enum sensor_channel chan, struct sensor_value *val) +{ + struct temp_lpc_data *data = dev->data; + + uint16_t Vbe1; + uint16_t Vbe8; + float slope; + float offset; + float temperature; + uint32_t cal_slope; + uint32_t cal_offset; + + const float alpha = FSL_FEATURE_LPADC_TEMP_PARAMETER_ALPHA; + const uintptr_t slope_addr = FSL_FEATURE_FLASH_NMPA_TEMP_SLOPE_ADDRS; + const uintptr_t offset_addr = FSL_FEATURE_FLASH_NMPA_TEMP_OFFSET_ADDRS; + + if (chan != SENSOR_CHAN_VOLTAGE && chan != SENSOR_CHAN_DIE_TEMP) { + return -ENOTSUP; + } + + /* Read the calibration from flash. */ + cal_slope = (*((volatile uint32_t *)(slope_addr))); + cal_offset = (*((volatile uint32_t *)(offset_addr))); + + if (((cal_slope & 0x1UL) != 0UL) && ((cal_offset & 0x1UL) != 0UL)) { + /* Re-justify based on the calibration values. */ + slope = ((float)(uint32_t)(cal_slope >> 1UL) / 1024.0f); + offset = ((float)(uint32_t)(cal_offset >> 1UL) / 1024.0f); + } else { + /* Otherwise, use the default slope and offset value. */ + slope = FSL_FEATURE_LPADC_TEMP_PARAMETER_A; + offset = FSL_FEATURE_LPADC_TEMP_PARAMETER_B; + } + + /* Read the 2 temperature sensor result. */ +#if (FSL_FEATURE_LPADC_TEMP_SENS_BUFFER_SIZE == 4U) + Vbe1 = data->buffer[2]; + Vbe8 = data->buffer[3]; +#else + Vbe1 = data->buffer[0]; + Vbe8 = data->buffer[1]; +#endif + + /* T = A*[alpha*(Vbe8-Vbe1)/(Vbe8 + alpha*(Vbe8-Vbe1))] - B. */ + temperature = slope * (alpha * ((float)Vbe8 - (float)Vbe1) / + ((float)Vbe8 + alpha * ((float)Vbe8 - (float)Vbe1))) - offset; + + return sensor_value_from_float(val, temperature); +} + +static DEVICE_API(sensor, temp_lpc_driver_api) = { + .sample_fetch = temp_lpc_sample_fetch, + .channel_get = temp_lpc_channel_get, +}; + +static int temp_lpc_init(const struct device *dev) +{ + const struct temp_lpc_config *config = dev->config; + const struct adc_channel_cfg *channels = config->adc_ch_cfg; + int ret; + + if (!device_is_ready(config->adc)) { + LOG_ERR("ADC device is not ready"); + return -EINVAL; + } + + ret = adc_channel_setup(config->adc, &channels[config->sensor_adc_ch]); + if (ret) { + LOG_ERR("failed to configure ADC channel (%d)", ret); + return ret; + } + + return 0; +} + +SENSOR_DEVICE_DT_INST_DEFINE(0, temp_lpc_init, NULL, &inst_data, + &inst_config, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, + &temp_lpc_driver_api); diff --git a/dts/arm/nxp/nxp_lpc55S1x_common.dtsi b/dts/arm/nxp/nxp_lpc55S1x_common.dtsi index 34a8eec8db2c..6b05be411ea0 100644 --- a/dts/arm/nxp/nxp_lpc55S1x_common.dtsi +++ b/dts/arm/nxp/nxp_lpc55S1x_common.dtsi @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -319,6 +320,22 @@ status = "okay"; }; + adc0: adc@a0000 { + compatible = "nxp,lpc-lpadc"; + reg = <0xa0000 0x1000>; + interrupts = <22 0>; + status = "disabled"; + clk-divider = <8>; + clk-source = <0>; + voltage-ref= <1>; + calibration-average = <128>; + power-level = <0>; + offset-value-a = <10>; + offset-value-b = <10>; + #io-channel-cells = <1>; + clocks = <&syscon MCUX_LPADC1_CLK>; + }; + usbhs: usbhs@94000 { compatible = "nxp,lpcip3511"; reg = <0x94000 0x1000>; diff --git a/dts/bindings/sensor/nxp,lpc-temperature.yaml b/dts/bindings/sensor/nxp,lpc-temperature.yaml new file mode 100644 index 000000000000..8a335e3a29d8 --- /dev/null +++ b/dts/bindings/sensor/nxp,lpc-temperature.yaml @@ -0,0 +1,14 @@ +# Copyright (c) 2025 Nova Dynamics LLC +# SPDX-License-Identifier: Apache-2.0 + +description: NXP LPC temperature sensor + +compatible: "nxp,lpc-temperature" + +include: sensor-device.yaml + +properties: + io-channels: + required: true + description: This should point to an ADC channel (e.g., <&adc0 0>). That + will be used for reading from the internal temperature sensor. diff --git a/samples/sensor/die_temp_polling/boards/lpcxpresso55s16.conf b/samples/sensor/die_temp_polling/boards/lpcxpresso55s16.conf new file mode 100644 index 000000000000..0b62af459025 --- /dev/null +++ b/samples/sensor/die_temp_polling/boards/lpcxpresso55s16.conf @@ -0,0 +1 @@ +CONFIG_TEMP_LPC=y diff --git a/samples/sensor/die_temp_polling/boards/lpcxpresso55s16.overlay b/samples/sensor/die_temp_polling/boards/lpcxpresso55s16.overlay new file mode 100644 index 000000000000..3b32b23b23f2 --- /dev/null +++ b/samples/sensor/die_temp_polling/boards/lpcxpresso55s16.overlay @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2025 Nova Dynamics LLC + */ + +#include + +/ { + aliases { + die-temp0 = &temp; + }; + + temp: temp { + status = "okay"; + compatible = "nxp,lpc-temperature"; + io-channels = <&adc0 0>; + }; +}; + +&adc0 { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,vref-mv = <1000>; + zephyr,input-positive = <26>; + zephyr,input-negative = <26>; + }; +};