-
Notifications
You must be signed in to change notification settings - Fork 6.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
- Loading branch information
Showing
7 changed files
with
329 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |