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
1 change: 1 addition & 0 deletions drivers/dac/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
2 changes: 2 additions & 0 deletions drivers/dac/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,6 @@ source "drivers/dac/Kconfig.renesas_ra"

source "drivers/dac/Kconfig.samd5x"

source "drivers/dac/Kconfig.mspm0"

endif # DAC
29 changes: 29 additions & 0 deletions drivers/dac/Kconfig.mspm0
Original file line number Diff line number Diff line change
@@ -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).
193 changes: 193 additions & 0 deletions drivers/dac/dac_mspm0.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
* Copyright (c) 2025 Linumiz GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT ti_mspm0_dac

#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/dac.h>
#include <zephyr/irq.h>
#include <zephyr/kernel.h>

/* TI Driverlib includes */
#include <ti/devices/msp/peripherals/hw_dac12.h>
#include <ti/driverlib/dl_dac12.h>

/* 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)
12 changes: 12 additions & 0 deletions dts/arm/ti/mspm0/g/mspm0g150x.dtsi
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
/* SPDX-License-Identifier: Apache-2.0 */

#include <ti/mspm0/g/mspm0g.dtsi>

/{
soc {
dac0: dac@40018000 {
compatible = "ti,mspm0-dac";
reg = <0x40018000 0x2000>;
interrupts = <7 0>;
status = "disabled";
#io-channel-cells = <1>;
};
};
};
12 changes: 12 additions & 0 deletions dts/arm/ti/mspm0/g/mspm0g350x.dtsi
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
/* SPDX-License-Identifier: Apache-2.0 */

#include <ti/mspm0/g/mspm0g.dtsi>

/{
soc {
dac0: dac@40018000 {
compatible = "ti,mspm0-dac";
reg = <0x40018000 0x2000>;
interrupts = <7 0>;
status = "disabled";
#io-channel-cells = <1>;
};
};
};
12 changes: 12 additions & 0 deletions dts/arm/ti/mspm0/g/mspm0gx51x.dtsi
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
/* SPDX-License-Identifier: Apache-2.0 */

#include <ti/mspm0/g/mspm0g.dtsi>

/{
soc {
dac0: dac@40018000 {
compatible = "ti,mspm0-dac";
reg = <0x40018000 0x2000>;
interrupts = <7 0>;
status = "disabled";
#io-channel-cells = <1>;
};
};
};
24 changes: 24 additions & 0 deletions dts/bindings/dac/ti,mspm0-dac.yaml
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions modules/Kconfig.mspm0
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ config USE_MSPM0_DL_UART

config USE_MSPM0_DL_TIMER
bool

config USE_MSPM0_DL_DAC12
bool
17 changes: 17 additions & 0 deletions samples/drivers/dac/boards/lp_mspm0g3507.overlay
Original file line number Diff line number Diff line change
@@ -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";
};
Loading