zephyr/drivers/dac/dac_dacx3608.c
Bartosz Bilas 035c75200e drivers: dac: dacx3608: set explicitly init priority
Since this DAC is connected via I2C bus the init priority
value must be higher than the default 50 so it can be initialized
later than the bus itself so add a dedicated init config
symbol for that.

Signed-off-by: Bartosz Bilas <b.bilas@grinn-global.com>
2022-07-15 09:59:51 +02:00

274 lines
6.1 KiB
C

/*
* Copyright (c) 2020 Matija Tudan
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/dac.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/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 {
struct i2c_dt_spec bus;
uint8_t resolution;
};
struct dacx3608_data {
uint8_t configured;
};
static int dacx3608_reg_read(const struct device *dev, uint8_t reg,
uint16_t *val)
{
const struct dacx3608_config *cfg = dev->config;
if (i2c_burst_read_dt(&cfg->bus, 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)
{
const struct dacx3608_config *cfg = dev->config;
uint8_t buf[3] = {reg, val >> 8, val & 0xFF};
return i2c_write_dt(&cfg->bus, buf, sizeof(buf));
}
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, &regval);
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;
if (!device_is_ready(config->bus.bus)) {
LOG_ERR("I2C device not ready");
return -ENODEV;
}
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 = { \
.bus = I2C_DT_SPEC_GET(INST_DT_DACX3608(n, t)), \
.resolution = res, \
}; \
DEVICE_DT_DEFINE(INST_DT_DACX3608(n, t), \
&dacx3608_init, NULL, \
&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) \
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);