Skip to content

Commit

Permalink
drivers: dac: Add TI DACx0501 driver
Browse files Browse the repository at this point in the history
Adds a DAC driver for Texas Instruments DACx0501 family of devices

Signed-off-by: Eran Gal <[email protected]>
Co-authored-by: Martin Jäger <[email protected]>
  • Loading branch information
2 people authored and aescolar committed Apr 17, 2024
1 parent e9562b6 commit e58dfac
Show file tree
Hide file tree
Showing 6 changed files with 255 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/dac/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ zephyr_library_sources_ifdef(CONFIG_DAC_MCUX_DAC32 dac_mcux_dac32.c)
zephyr_library_sources_ifdef(CONFIG_DAC_STM32 dac_stm32.c)
zephyr_library_sources_ifdef(CONFIG_DAC_SAM dac_sam.c)
zephyr_library_sources_ifdef(CONFIG_DAC_SAM0 dac_sam0.c)
zephyr_library_sources_ifdef(CONFIG_DAC_DACX0501 dac_dacx0501.c)
zephyr_library_sources_ifdef(CONFIG_DAC_DACX0508 dac_dacx0508.c)
zephyr_library_sources_ifdef(CONFIG_DAC_DACX3608 dac_dacx3608.c)
zephyr_library_sources_ifdef(CONFIG_DAC_LTC166X dac_ltc166x.c)
Expand Down
2 changes: 2 additions & 0 deletions drivers/dac/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ source "drivers/dac/Kconfig.sam"

source "drivers/dac/Kconfig.sam0"

source "drivers/dac/Kconfig.dacx0501"

source "drivers/dac/Kconfig.dacx0508"

source "drivers/dac/Kconfig.dacx3608"
Expand Down
23 changes: 23 additions & 0 deletions drivers/dac/Kconfig.dacx0501
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# DAC configuration options

# Copyright (c) 2023 Google, LLC.
#
# SPDX-License-Identifier: Apache-2.0

config DAC_DACX0501
bool "TI DACx0501 DAC driver"
default y
depends on DT_HAS_TI_DACX0501_ENABLED
select I2C
help
Enable the driver for the TI DACx0501.

if DAC_DACX0501

config DAC_DACX0501_INIT_PRIORITY
int "Init priority"
default 80
help
TI DACx0501 DAC device driver initialization priority. Must be greater than I2C_INIT_PRIORITY.

endif # DAC_DACX0501
191 changes: 191 additions & 0 deletions drivers/dac/dac_dacx0501.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/*
* Copyright (c) 2024 Google LLC.
*
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @brief Driver for Texas Instruments DACx0501 series
*
* Device driver for the Texas Instruments DACx0501 series of devices: DAC60501, DAC70501 and
* DAC80501: Digital to Analog Converters with a single channel output and with 12, 14 and 16
* bits resolution respectively. Data sheet can be found here:
* https://www.ti.com/lit/ds/symlink/dac80501.pdf
*/

#include <zephyr/kernel.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/dac.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/byteorder.h>

LOG_MODULE_REGISTER(dac_dacx0501, CONFIG_DAC_LOG_LEVEL);

#define DACX0501_REG_DEVICE_ID 0x01U
#define DACX0501_REG_SYNC 0x02U
#define DACX0501_REG_CONFIG 0x03U
#define DACX0501_REG_GAIN 0x04U
#define DACX0501_REG_TRIGGER 0x05U
#define DACX0501_REG_STATUS 0x07U
#define DACX0501_REG_DAC 0x08U

#define DACX0501_MASK_DEVICE_ID_RES GENMASK(14, 12)
#define DACX0501_MASK_CONFIG_REF_PWDWN BIT(8)
#define DACX0501_MASK_CONFIG_DAC_PWDWN BIT(0)
#define DACX0501_MASK_GAIN_BUFF_GAIN BIT(0)
#define DACX0501_MASK_GAIN_REFDIV_EN BIT(8)
#define DACX0501_MASK_TRIGGER_SOFT_RESET (BIT(1) | BIT(3))
#define DACX0501_MASK_STATUS_REF_ALM BIT(0)

/* Specifies the source of the reference voltage. */
enum voltage_reference_source {
REF_INTERNAL, /* Internal 2.5V voltage reference. */
REF_EXTERNAL, /* External pin voltage reference. */
};

/* Specifies the reference voltage multiplier. */
enum output_gain {
VM_MUL2, /* Multiplies by 2. */
VM_MUL1, /* Multiplies by 1. */
VM_DIV2, /* Multiplies by 0.5 */
};

struct dacx0501_config {
struct i2c_dt_spec i2c_spec;
enum voltage_reference_source voltage_reference;
enum output_gain output_gain;
};

struct dacx0501_data {
/* Number of bits in the DAC register: Either 12, 14 or 16. */
uint8_t resolution;
};

static int dacx0501_reg_read(const struct device *dev, const uint8_t addr, uint16_t *data)
{
const struct dacx0501_config *config = dev->config;
uint8_t raw_data[2];
int status;

status = i2c_write_read_dt(&config->i2c_spec, &addr, sizeof(addr), raw_data,
sizeof(raw_data));
if (status != 0) {
return status;
}

/* DAC is big endian. */
*data = sys_get_be16(raw_data);
return 0;
}

static int dacx0501_reg_write(const struct device *dev, uint8_t addr, uint16_t data)
{
const struct dacx0501_config *config = dev->config;
uint8_t write_cmd[3] = {addr};

/* DAC is big endian. */
sys_put_be16(data, write_cmd + 1);

return i2c_write_dt(&config->i2c_spec, write_cmd, sizeof(write_cmd));
}

static int dacx0501_channel_setup(const struct device *dev,
const struct dac_channel_cfg *channel_cfg)
{
struct dacx0501_data *data = dev->data;

/* DACx0501 series only has a single output channel. */
if (channel_cfg->channel_id != 0) {
LOG_ERR("Unsupported channel %d", channel_cfg->channel_id);
return -ENOTSUP;
}

if (channel_cfg->resolution != data->resolution) {
LOG_ERR("Unsupported resolution %d. Actual: %d", channel_cfg->resolution,
data->resolution);
return -ENOTSUP;
}

return 0;
}

static int dacx0501_write_value(const struct device *dev, uint8_t channel, uint32_t value)
{
struct dacx0501_data *data = dev->data;

if (channel != 0) {
LOG_ERR("dacx0501: Unsupported channel %d", channel);
return -ENOTSUP;
}

if (value >= (1 << data->resolution)) {
LOG_ERR("dacx0501: Value %d out of range", value);
return -EINVAL;
}

value <<= (16 - data->resolution);

return dacx0501_reg_write(dev, DACX0501_REG_DAC, value);
}

static int dacx0501_init(const struct device *dev)
{
const struct dacx0501_config *config = dev->config;
struct dacx0501_data *data = dev->data;
uint16_t device_id;
int status;

if (!i2c_is_ready_dt(&config->i2c_spec)) {
LOG_ERR("I2C bus %s not ready", config->i2c_spec.bus->name);
return -ENODEV;
}

status = dacx0501_reg_read(dev, DACX0501_REG_DEVICE_ID, &device_id);
if (status != 0) {
LOG_ERR("read DEVICE_ID register failed");
return status;
}

/* See DEVICE_ID register RES field in the data sheet. */
data->resolution = 16 - 2 * FIELD_GET(DACX0501_MASK_DEVICE_ID_RES, device_id);

status = dacx0501_reg_write(dev, DACX0501_REG_CONFIG,
FIELD_PREP(DACX0501_MASK_CONFIG_REF_PWDWN,
config->voltage_reference == REF_EXTERNAL));
if (status != 0) {
LOG_ERR("write CONFIG register failed");
return status;
}

status = dacx0501_reg_write(
dev, DACX0501_REG_GAIN,
FIELD_PREP(DACX0501_MASK_GAIN_REFDIV_EN, config->output_gain == VM_DIV2) |
FIELD_PREP(DACX0501_MASK_GAIN_BUFF_GAIN, config->output_gain == VM_MUL2));
if (status != 0) {
LOG_ERR("GAIN Register update failed");
return status;
}

return 0;
}

static const struct dac_driver_api dacx0501_driver_api = {
.channel_setup = dacx0501_channel_setup,
.write_value = dacx0501_write_value,
};

#define DT_DRV_COMPAT ti_dacx0501

#define DACX0501_DEFINE(n) \
static struct dacx0501_data dacx0501_data_##n = {}; \
static const struct dacx0501_config dacx0501_config_##n = { \
.i2c_spec = I2C_DT_SPEC_INST_GET(n), \
.voltage_reference = \
_CONCAT(REF_, DT_STRING_UPPER_TOKEN(DT_DRV_INST(n), voltage_reference)), \
.output_gain = _CONCAT(VM_, DT_STRING_UPPER_TOKEN(DT_DRV_INST(n), output_gain)), \
}; \
DEVICE_DT_INST_DEFINE(n, &dacx0501_init, NULL, &dacx0501_data_##n, &dacx0501_config_##n, \
POST_KERNEL, CONFIG_DAC_DACX0501_INIT_PRIORITY, \
&dacx0501_driver_api);

DT_INST_FOREACH_STATUS_OKAY(DACX0501_DEFINE)
30 changes: 30 additions & 0 deletions dts/bindings/dac/ti,dacx0501.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright (c) 2024 Google, LLC.
# SPDX-License-Identifier: Apache-2.0

include: base.yaml

description: TI DACx0501 12 to 16 bit DAC series for DAC60501, DAC70501 and DAC80501 devices.

compatible: "ti,dacx0501"

properties:
voltage-reference:
type: string
required: true
enum:
- "internal"
- "external"
description: |
DAC voltage reference select: either internal (2.5 V) or external
output-gain:
type: string
required: true
enum:
- "mul2"
- "mul1"
- "div2"
description: |
This setting can be used to control the output voltage range within the supported bit
resolution. mul2 will double the output range but lower the resolution, while div2 will
lower the range but double the resolution.
8 changes: 8 additions & 0 deletions tests/drivers/build_all/dac/app.overlay
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@
voltage_reference = <0>;
power_down_mode = <0>;
};

test_i2c_dacx0501:dacx0501@62 {
compatible = "ti,dacx0501";
status = "okay";
reg = <0x62>;
voltage-reference = "internal";
output-gain = "mul2";
};
};

test_spi: spi@33334444 {
Expand Down

0 comments on commit e58dfac

Please sign in to comment.