drivers: dac: added driver for TI DACx3608
The DAC53608 and DAC43608 (DACx3608) are lowpower, eight-channel, voltage-output, 10-bit or 8-bit digital-to-analog converters (DACs) respectively. They support I2C with a wide power supply range from 1.8 V to 5.5 V, and a full scale output voltage range of 1.8 V to 5.5 V. The DACx3608 also includes per channel, user programmable, power down registers. Signed-off-by: Matija Tudan <mtudan@mobilisis.hr>
This commit is contained in:
parent
4e7b5769bd
commit
1463596205
|
@ -7,5 +7,6 @@ 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_SAM0 dac_sam0.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_SHELL dac_shell.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_USERSPACE dac_handlers.c)
|
||||
|
|
|
@ -32,4 +32,6 @@ source "drivers/dac/Kconfig.sam0"
|
|||
|
||||
source "drivers/dac/Kconfig.dacx0508"
|
||||
|
||||
source "drivers/dac/Kconfig.dacx3608"
|
||||
|
||||
endif # DAC
|
||||
|
|
18
drivers/dac/Kconfig.dacx3608
Normal file
18
drivers/dac/Kconfig.dacx3608
Normal file
|
@ -0,0 +1,18 @@
|
|||
# DAC configuration options
|
||||
|
||||
# Copyright (c) 2020 Matija Tudan
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config DAC_DACX3608
|
||||
bool "TI DACX3608 DAC driver"
|
||||
depends on I2C
|
||||
help
|
||||
Enable the driver for the TI DACX3608.
|
||||
|
||||
config DAC_DACX3608_INIT_PRIORITY
|
||||
int "Init priority"
|
||||
depends on DAC_DACX3608
|
||||
default 80
|
||||
help
|
||||
DACX3608 DAC device driver initialization priority.
|
281
drivers/dac/dac_dacx3608.c
Normal file
281
drivers/dac/dac_dacx3608.c
Normal file
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Matija Tudan
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <kernel.h>
|
||||
#include <drivers/i2c.h>
|
||||
#include <drivers/dac.h>
|
||||
#include <sys/util.h>
|
||||
#include <sys/byteorder.h>
|
||||
#include <sys/__assert.h>
|
||||
#include <logging/log.h>
|
||||
|
||||
LOG_MODULE_REGISTER(dac_dacx3608, CONFIG_DAC_LOG_LEVEL);
|
||||
|
||||
/* Register addresses */
|
||||
#define DACX3608_REG_DEVICE_CONFIG 0x01U
|
||||
#define DACX3608_REG_STATUS_TRIGGER 0x02U
|
||||
#define DACX3608_REG_BRDCAST 0x03U
|
||||
#define DACX3608_REG_DACA_DATA 0x08U
|
||||
|
||||
#define DAC43608_DEVICE_ID 0x500 /* STATUS_TRIGGER[DEVICE_ID] */
|
||||
#define DAC53608_DEVICE_ID 0x300 /* STATUS_TRIGGER[DEVICE_ID] */
|
||||
#define DACX3608_SW_RST 0x0A /* STATUS_TRIGGER[SW_RST] */
|
||||
#define DACX3608_POR_DELAY 5
|
||||
#define DACX3608_MAX_CHANNEL 8
|
||||
|
||||
struct dacx3608_config {
|
||||
const char *i2c_bus;
|
||||
uint16_t i2c_addr;
|
||||
uint8_t resolution;
|
||||
};
|
||||
|
||||
struct dacx3608_data {
|
||||
const struct device *i2c;
|
||||
uint8_t configured;
|
||||
};
|
||||
|
||||
static int dacx3608_reg_read(const struct device *dev, uint8_t reg,
|
||||
uint16_t *val)
|
||||
{
|
||||
struct dacx3608_data *data = dev->data;
|
||||
const struct dacx3608_config *cfg = dev->config;
|
||||
|
||||
if (i2c_burst_read(data->i2c, cfg->i2c_addr,
|
||||
reg, (uint8_t *) val, 2) < 0) {
|
||||
LOG_ERR("I2C read failed");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*val = sys_be16_to_cpu(*val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dacx3608_reg_write(const struct device *dev, uint8_t reg,
|
||||
uint16_t val)
|
||||
{
|
||||
struct dacx3608_data *data = dev->data;
|
||||
const struct dacx3608_config *cfg = dev->config;
|
||||
uint8_t buf[3] = {reg, val >> 8, val & 0xFF};
|
||||
|
||||
return i2c_write(data->i2c, buf, sizeof(buf), cfg->i2c_addr);
|
||||
}
|
||||
|
||||
int dacx3608_reg_update(const struct device *dev, uint8_t reg,
|
||||
uint16_t mask, bool setting)
|
||||
{
|
||||
uint16_t regval;
|
||||
int ret;
|
||||
|
||||
ret = dacx3608_reg_read(dev, reg, ®val);
|
||||
if (ret) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (setting) {
|
||||
regval |= mask;
|
||||
} else {
|
||||
regval &= ~mask;
|
||||
}
|
||||
|
||||
ret = dacx3608_reg_write(dev, reg, regval);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dacx3608_channel_setup(const struct device *dev,
|
||||
const struct dac_channel_cfg *channel_cfg)
|
||||
{
|
||||
const struct dacx3608_config *config = dev->config;
|
||||
struct dacx3608_data *data = dev->data;
|
||||
bool setting = false;
|
||||
int ret;
|
||||
|
||||
if (channel_cfg->channel_id > DACX3608_MAX_CHANNEL - 1) {
|
||||
LOG_ERR("Unsupported channel %d", channel_cfg->channel_id);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (channel_cfg->resolution != config->resolution) {
|
||||
LOG_ERR("Unsupported resolution %d", channel_cfg->resolution);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (data->configured & BIT(channel_cfg->channel_id)) {
|
||||
LOG_DBG("Channel %d already configured", channel_cfg->channel_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Clear PDNn bit */
|
||||
ret = dacx3608_reg_update(dev, DACX3608_REG_DEVICE_CONFIG,
|
||||
BIT(channel_cfg->channel_id), setting);
|
||||
if (ret) {
|
||||
LOG_ERR("Unable to update DEVICE_CONFIG register");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
data->configured |= BIT(channel_cfg->channel_id);
|
||||
|
||||
LOG_DBG("Channel %d initialized", channel_cfg->channel_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dacx3608_write_value(const struct device *dev, uint8_t channel,
|
||||
uint32_t value)
|
||||
{
|
||||
const struct dacx3608_config *config = dev->config;
|
||||
struct dacx3608_data *data = dev->data;
|
||||
uint16_t regval;
|
||||
int ret;
|
||||
|
||||
if (channel > DACX3608_MAX_CHANNEL - 1) {
|
||||
LOG_ERR("Unsupported channel %d", channel);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (!(data->configured & BIT(channel))) {
|
||||
LOG_ERR("Channel %d not initialized", channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (value >= (1 << (config->resolution))) {
|
||||
LOG_ERR("Value %d out of range", value);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shift passed value two times left because first two bits are Don't Care
|
||||
*
|
||||
* DACn_DATA register format:
|
||||
*
|
||||
* | 15 14 13 12 | 11 10 9 8 7 6 5 4 3 2 | 1 0 |
|
||||
* |-------------|---------------------------------|------------|
|
||||
* | Don't Care | DAC53608[9:0] / DAC43608[7:0] | Don't Care |
|
||||
*/
|
||||
regval = value << 2;
|
||||
regval &= 0xFFFF;
|
||||
|
||||
ret = dacx3608_reg_write(dev, DACX3608_REG_DACA_DATA + channel, regval);
|
||||
if (ret) {
|
||||
LOG_ERR("Unable to set value %d on channel %d", value, channel);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dacx3608_soft_reset(const struct device *dev)
|
||||
{
|
||||
uint16_t regval = DACX3608_SW_RST;
|
||||
int ret;
|
||||
|
||||
ret = dacx3608_reg_write(dev, DACX3608_REG_STATUS_TRIGGER, regval);
|
||||
if (ret) {
|
||||
return -EIO;
|
||||
}
|
||||
k_msleep(DACX3608_POR_DELAY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dacx3608_device_id_check(const struct device *dev)
|
||||
{
|
||||
uint16_t dev_id;
|
||||
int ret;
|
||||
|
||||
ret = dacx3608_reg_read(dev, DACX3608_REG_STATUS_TRIGGER, dev_id);
|
||||
if (ret) {
|
||||
LOG_ERR("Unable to read device ID");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
switch (dev_id) {
|
||||
case DAC43608_DEVICE_ID:
|
||||
case DAC53608_DEVICE_ID:
|
||||
LOG_DBG("Device ID %#4x", dev_id);
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Unknown Device ID %#4x", dev_id);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dacx3608_init(const struct device *dev)
|
||||
{
|
||||
const struct dacx3608_config *config = dev->config;
|
||||
struct dacx3608_data *data = dev->data;
|
||||
int ret;
|
||||
|
||||
data->i2c = device_get_binding(config->i2c_bus);
|
||||
if (!data->i2c) {
|
||||
LOG_ERR("Could not find I2C device");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = dacx3608_soft_reset(dev);
|
||||
if (ret) {
|
||||
LOG_ERR("Soft-reset failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dacx3608_device_id_check(dev);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->configured = 0;
|
||||
|
||||
LOG_DBG("Init complete");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dac_driver_api dacx3608_driver_api = {
|
||||
.channel_setup = dacx3608_channel_setup,
|
||||
.write_value = dacx3608_write_value,
|
||||
};
|
||||
|
||||
#define INST_DT_DACX3608(inst, t) DT_INST(inst, ti_dac##t)
|
||||
|
||||
#define DACX3608_DEVICE(t, n, res) \
|
||||
static struct dacx3608_data dac##t##_data_##n; \
|
||||
static const struct dacx3608_config dac##t##_config_##n = { \
|
||||
.i2c_bus = DT_BUS_LABEL(INST_DT_DACX3608(n, t)), \
|
||||
.i2c_addr = DT_REG_ADDR(INST_DT_DACX3608(n, t)), \
|
||||
.resolution = res, \
|
||||
}; \
|
||||
DEVICE_DT_DEFINE(INST_DT_DACX3608(n, t), \
|
||||
&dacx3608_init, device_pm_control_nop, \
|
||||
&dac##t##_data_##n, \
|
||||
&dac##t##_config_##n, POST_KERNEL, \
|
||||
CONFIG_DAC_DACX3608_INIT_PRIORITY, \
|
||||
&dacx3608_driver_api)
|
||||
|
||||
/*
|
||||
* DAC43608: 8-bit
|
||||
*/
|
||||
#define DAC43608_DEVICE(n) DACX3608_DEVICE(43608, n, 8)
|
||||
|
||||
/*
|
||||
* DAC53608: 10-bit
|
||||
*/
|
||||
#define DAC53608_DEVICE(n) DACX3608_DEVICE(53608, n, 10)
|
||||
|
||||
#define CALL_WITH_ARG(arg, expr) expr(arg)
|
||||
|
||||
#define INST_DT_DACX3608_FOREACH(t, inst_expr) \
|
||||
UTIL_LISTIFY(DT_NUM_INST_STATUS_OKAY(ti_dac##t), \
|
||||
CALL_WITH_ARG, inst_expr)
|
||||
|
||||
INST_DT_DACX3608_FOREACH(43608, DAC43608_DEVICE);
|
||||
INST_DT_DACX3608_FOREACH(53608, DAC53608_DEVICE);
|
8
dts/bindings/dac/ti,dac43608.yaml
Normal file
8
dts/bindings/dac/ti,dac43608.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2020 Matija Tudan
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: TI DAC43608 8-bit 8 channel DAC
|
||||
|
||||
compatible: "ti,dac43608"
|
||||
|
||||
include: ti,dacx3608-base.yaml
|
8
dts/bindings/dac/ti,dac53608.yaml
Normal file
8
dts/bindings/dac/ti,dac53608.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2020 Matija Tudan
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: TI DAC53608 10-bit 8 channel DAC
|
||||
|
||||
compatible: "ti,dac53608"
|
||||
|
||||
include: ti,dacx3608-base.yaml
|
11
dts/bindings/dac/ti,dacx3608-base.yaml
Normal file
11
dts/bindings/dac/ti,dacx3608-base.yaml
Normal file
|
@ -0,0 +1,11 @@
|
|||
# Copyright (c) 2020 Matija Tudan
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
include: [dac-controller.yaml, i2c-device.yaml]
|
||||
|
||||
properties:
|
||||
"#io-channel-cells":
|
||||
const: 1
|
||||
|
||||
io-channel-cells:
|
||||
- output
|
Loading…
Reference in a new issue