diff --git a/drivers/dac/CMakeLists.txt b/drivers/dac/CMakeLists.txt index 4e69815f4eca4..c6810cc0437cd 100644 --- a/drivers/dac/CMakeLists.txt +++ b/drivers/dac/CMakeLists.txt @@ -30,3 +30,4 @@ zephyr_library_sources_ifdef(CONFIG_DAC_MCUX_GAU dac_mcux_gau.c) zephyr_library_sources_ifdef(CONFIG_DAC_TEST dac_test.c) zephyr_library_sources_ifdef(CONFIG_DAC_MAX22017 dac_max22017.c) zephyr_library_sources_ifdef(CONFIG_DAC_RENESAS_RA dac_renesas_ra.c) +zephyr_library_sources_ifdef(CONFIG_DAC_MSPM0 dac_mspm0.c) diff --git a/drivers/dac/Kconfig b/drivers/dac/Kconfig index 8305189987135..5f6a849d6d274 100644 --- a/drivers/dac/Kconfig +++ b/drivers/dac/Kconfig @@ -69,4 +69,6 @@ source "drivers/dac/Kconfig.renesas_ra" source "drivers/dac/Kconfig.samd5x" +source "drivers/dac/Kconfig.mspm0" + endif # DAC diff --git a/drivers/dac/Kconfig.mspm0 b/drivers/dac/Kconfig.mspm0 new file mode 100644 index 0000000000000..16f7f9315e080 --- /dev/null +++ b/drivers/dac/Kconfig.mspm0 @@ -0,0 +1,29 @@ +# TI MSPM0 DAC Driver Configuration + +# Copyright (c) 2025 Linumiz GmbH +# SPDX-License-Identifier: Apache-2.0 + +config DAC_MSPM0 + bool "TI MSPM0 Digital To Analog Converter (DAC) Driver" + default y + depends on DT_HAS_TI_MSPM0_DAC_ENABLED + select USE_MSPM0_DL_DAC12 + help + Enable support for the DAC (Digital-to-Analog Converter) + peripheral driver for Texas Instruments MSPM0 G-Series MCUs. + +config DAC_MSPM0_TIMEOUT_MS + int "DAC MSPM0 module ready timeout in milliseconds" + default 50 + depends on DAC_MSPM0 + help + Timeout in milliseconds to wait for the DAC module to be + ready after enabling. + +config DAC_MSPM0_VREF_SOURCE + bool "Select external VREF+/VREF- as voltage reference" + default n + depends on DAC_MSPM0 + help + Select external voltage reference (VREF+/VREF-) for DAC + instead of the internal reference supply (VDDA/VSSA). diff --git a/drivers/dac/dac_mspm0.c b/drivers/dac/dac_mspm0.c new file mode 100644 index 0000000000000..6bad0c146803e --- /dev/null +++ b/drivers/dac/dac_mspm0.c @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2025 Linumiz GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ti_mspm0_dac + +#include +#include +#include +#include +#include + +/* TI Driverlib includes */ +#include +#include + +/* DAC valid resolutions */ +#define DAC_RESOLUTION_8BIT 8 +#define DAC_RESOLUTION_12BIT 12 + +/* 8-bit Binary Repr Range */ +#define DAC8_BINARY_REPR_MIN 0 +#define DAC8_BINARY_REPR_MAX 255 + +/* 12-bit Binary Repr Range */ +#define DAC12_BINARY_REPR_MIN 0 +#define DAC12_BINARY_REPR_MAX 4095 + +/* DAC Primary Channel ID */ +#define DAC_CHANNEL_ID_PRIMARY 0 + +/* Timeout to wait until the DAC module is ready after enabling */ +#define DAC_MOD_RDY_TIMEOUT K_MSEC(CONFIG_DAC_MSPM0_TIMEOUT_MS) + +struct dac_mspm0_config { + DAC12_Regs *dac_base; + void (*irq_config_func)(const struct device *dev); +}; + +struct dac_mspm0_data { + uint8_t resolution; + struct k_mutex lock; + struct k_sem mod_rdy; +}; + +static void dac_mspm0_isr(const struct device *dev) +{ + const struct dac_mspm0_config *config = dev->config; + struct dac_mspm0_data *data = dev->data; + + if (DL_DAC12_getPendingInterrupt(config->dac_base) == DL_DAC12_IIDX_MODULE_READY) { + k_sem_give(&data->mod_rdy); + } +} + +static int dac_mspm0_channel_setup(const struct device *dev, + const struct dac_channel_cfg *channel_cfg) +{ + const struct dac_mspm0_config *config = dev->config; + struct dac_mspm0_data *data = dev->data; + int ret = 0; + + if (channel_cfg->channel_id != DAC_CHANNEL_ID_PRIMARY) { + return -EINVAL; + } + + if (channel_cfg->resolution != DAC_RESOLUTION_8BIT && + channel_cfg->resolution != DAC_RESOLUTION_12BIT) { + return -ENOTSUP; + } + + k_mutex_lock(&data->lock, K_FOREVER); + k_sem_reset(&data->mod_rdy); + + /* DAC must be disabled before configuration */ + DL_DAC12_disable(config->dac_base); + + DL_DAC12_configDataFormat(config->dac_base, DL_DAC12_REPRESENTATION_BINARY, + (channel_cfg->resolution == DAC_RESOLUTION_12BIT) ? + DL_DAC12_RESOLUTION_12BIT : DL_DAC12_RESOLUTION_8BIT); + + /* buffered must be true to enable amplifier for output drive */ + DL_DAC12_setAmplifier(config->dac_base, + (channel_cfg->buffered) ? DL_DAC12_AMP_ON : DL_DAC12_AMP_OFF_0V); + + if (IS_ENABLED(CONFIG_DAC_MSPM0_VREF_SOURCE)) { + DL_DAC12_setReferenceVoltageSource(config->dac_base, + DL_DAC12_VREF_SOURCE_VEREFP_VEREFN); + } else { + DL_DAC12_setReferenceVoltageSource(config->dac_base, + DL_DAC12_VREF_SOURCE_VDDA_VSSA); + } + + /* internal must be true to route output to OPA, ADC, COMP and DAC_OUT pin */ + if (channel_cfg->internal) { + DL_DAC12_enableOutputPin(config->dac_base); + } else { + DL_DAC12_disableOutputPin(config->dac_base); + } + + DL_DAC12_enable(config->dac_base); + + if (k_sem_take(&data->mod_rdy, DAC_MOD_RDY_TIMEOUT) != 0) { + ret = -ETIMEDOUT; + goto unlock; + } + + data->resolution = channel_cfg->resolution; + DL_DAC12_performSelfCalibrationBlocking(config->dac_base); + +unlock: + k_mutex_unlock(&data->lock); + return ret; +} + +static int dac_mspm0_write_value(const struct device *dev, uint8_t channel, uint32_t value) +{ + const struct dac_mspm0_config *config = dev->config; + struct dac_mspm0_data *data = dev->data; + int ret = 0; + + k_mutex_lock(&data->lock, K_FOREVER); + + /* Validate channel and resolution */ + if (channel != DAC_CHANNEL_ID_PRIMARY || data->resolution == 0) { + ret = -EINVAL; + goto unlock; + } + + if (data->resolution == DAC_RESOLUTION_12BIT) { + if (value > DAC12_BINARY_REPR_MAX || value < DAC12_BINARY_REPR_MIN) { + ret = -EINVAL; + goto unlock; + } + DL_DAC12_output12(config->dac_base, value); + + } else { + if (value > DAC8_BINARY_REPR_MAX || value < DAC8_BINARY_REPR_MIN) { + ret = -EINVAL; + goto unlock; + } + DL_DAC12_output8(config->dac_base, (uint8_t)value); + } + +unlock: + k_mutex_unlock(&data->lock); + return ret; +} + +static int dac_mspm0_init(const struct device *dev) +{ + const struct dac_mspm0_config *config = dev->config; + + DL_DAC12_enablePower(config->dac_base); + + config->irq_config_func(dev); + DL_DAC12_enableInterrupt(config->dac_base, DL_DAC12_INTERRUPT_MODULE_READY); + + return 0; +} + +static DEVICE_API(dac, dac_mspm0_driver_api) = { + .channel_setup = dac_mspm0_channel_setup, + .write_value = dac_mspm0_write_value +}; + +#define DAC_MSPM0_DEFINE(id) \ + \ + static void dac_mspm0_irq_config_##id(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(id), DT_INST_IRQ(id, priority), dac_mspm0_isr, \ + DEVICE_DT_INST_GET(id), 0); \ + irq_enable(DT_INST_IRQN(id)); \ + }; \ + \ + static const struct dac_mspm0_config dac_mspm0_config_##id = { \ + .dac_base = (DAC12_Regs *)DT_INST_REG_ADDR(id), \ + .irq_config_func = dac_mspm0_irq_config_##id, \ + }; \ + \ + static struct dac_mspm0_data dac_mspm0_data_##id = { \ + /* Configure resolution at channel setup */ \ + .lock = Z_MUTEX_INITIALIZER(dac_mspm0_data_##id.lock), \ + .mod_rdy = Z_SEM_INITIALIZER(dac_mspm0_data_##id.mod_rdy, 0, 1), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(id, &dac_mspm0_init, NULL, &dac_mspm0_data_##id, \ + &dac_mspm0_config_##id, POST_KERNEL, CONFIG_DAC_INIT_PRIORITY, \ + &dac_mspm0_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(DAC_MSPM0_DEFINE) diff --git a/dts/arm/ti/mspm0/g/mspm0g150x.dtsi b/dts/arm/ti/mspm0/g/mspm0g150x.dtsi index 7662a074329d6..9a65fb3289368 100644 --- a/dts/arm/ti/mspm0/g/mspm0g150x.dtsi +++ b/dts/arm/ti/mspm0/g/mspm0g150x.dtsi @@ -1,3 +1,15 @@ /* SPDX-License-Identifier: Apache-2.0 */ #include + +/{ + soc { + dac0: dac@40018000 { + compatible = "ti,mspm0-dac"; + reg = <0x40018000 0x2000>; + interrupts = <7 0>; + status = "disabled"; + #io-channel-cells = <1>; + }; + }; +}; diff --git a/dts/arm/ti/mspm0/g/mspm0g350x.dtsi b/dts/arm/ti/mspm0/g/mspm0g350x.dtsi index 7662a074329d6..9a65fb3289368 100644 --- a/dts/arm/ti/mspm0/g/mspm0g350x.dtsi +++ b/dts/arm/ti/mspm0/g/mspm0g350x.dtsi @@ -1,3 +1,15 @@ /* SPDX-License-Identifier: Apache-2.0 */ #include + +/{ + soc { + dac0: dac@40018000 { + compatible = "ti,mspm0-dac"; + reg = <0x40018000 0x2000>; + interrupts = <7 0>; + status = "disabled"; + #io-channel-cells = <1>; + }; + }; +}; diff --git a/dts/arm/ti/mspm0/g/mspm0gx51x.dtsi b/dts/arm/ti/mspm0/g/mspm0gx51x.dtsi index 7662a074329d6..9a65fb3289368 100644 --- a/dts/arm/ti/mspm0/g/mspm0gx51x.dtsi +++ b/dts/arm/ti/mspm0/g/mspm0gx51x.dtsi @@ -1,3 +1,15 @@ /* SPDX-License-Identifier: Apache-2.0 */ #include + +/{ + soc { + dac0: dac@40018000 { + compatible = "ti,mspm0-dac"; + reg = <0x40018000 0x2000>; + interrupts = <7 0>; + status = "disabled"; + #io-channel-cells = <1>; + }; + }; +}; diff --git a/dts/bindings/dac/ti,mspm0-dac.yaml b/dts/bindings/dac/ti,mspm0-dac.yaml new file mode 100644 index 0000000000000..1fdbf43b28a5c --- /dev/null +++ b/dts/bindings/dac/ti,mspm0-dac.yaml @@ -0,0 +1,24 @@ +# Copyright (c) 2025 Linumiz GmbH +# SPDX-License-Identifier: Apache-2.0 + +description: Texas Instruments MSPM0 G-Series Digital-to-Analog Converter (DAC) + +compatible: "ti,mspm0-dac" + +include: dac-controller.yaml + +properties: + reg: + required: true + + interrupts: + required: true + + vref: + type: phandle + + "#io-channel-cells": + const: 1 + +io-channel-cells: + - output diff --git a/modules/Kconfig.mspm0 b/modules/Kconfig.mspm0 index 7125ff4919672..9f460840315e2 100644 --- a/modules/Kconfig.mspm0 +++ b/modules/Kconfig.mspm0 @@ -14,3 +14,6 @@ config USE_MSPM0_DL_UART config USE_MSPM0_DL_TIMER bool + +config USE_MSPM0_DL_DAC12 + bool diff --git a/samples/drivers/dac/boards/lp_mspm0g3507.overlay b/samples/drivers/dac/boards/lp_mspm0g3507.overlay new file mode 100644 index 0000000000000..b1c4638c0aad3 --- /dev/null +++ b/samples/drivers/dac/boards/lp_mspm0g3507.overlay @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2025 Linumiz GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + zephyr,user { + dac = <&dac0>; + dac-channel-id = <0>; + dac-resolution = <12>; + }; +}; + +&dac0 { + status = "okay"; +};