Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

drivers: dac: add max22017 support #76017

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
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 @@ -25,3 +25,4 @@ zephyr_library_sources_ifdef(CONFIG_DAC_AD569X dac_ad569x.c)
zephyr_library_sources_ifdef(CONFIG_USERSPACE dac_handlers.c)
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)
2 changes: 2 additions & 0 deletions drivers/dac/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,6 @@ source "drivers/dac/Kconfig.ad569x"

source "drivers/dac/Kconfig.test"

source "drivers/dac/Kconfig.max22017"

endif # DAC
21 changes: 21 additions & 0 deletions drivers/dac/Kconfig.max22017
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright (c) 2024 Analog Devices Inc.
# Copyright (c) 2024 BayLibre SAS
# SPDX-License-Identifier: Apache-2.0

config DAC_MAX22017
bool "Analog Devices MAX22017 DAC"
default y
depends on DT_HAS_ADI_MAX22017_DAC_ENABLED
select MFD
help
Enable the driver for the Analog Devices MAX22017 DAC

if DAC_MAX22017

config DAC_MAX22017_INIT_PRIORITY
int "Init priority"
default 80
help
Analog Devices MAX22017 DAC device driver initialization priority.

endif # DAC_MAX22017
237 changes: 237 additions & 0 deletions drivers/dac/dac_max22017.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
/*
* Copyright (c) 2024 Analog Devices Inc.
* Copyright (c) 2024 Baylibre SAS
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/drivers/dac.h>

#include <zephyr/drivers/mfd/max22017.h>
#include <zephyr/logging/log.h>

#define DT_DRV_COMPAT adi_max22017_dac
LOG_MODULE_REGISTER(dac_max22017, CONFIG_DAC_LOG_LEVEL);

struct dac_adi_max22017_config {
const struct device *parent;
uint8_t resolution;
uint8_t nchannels;
const struct gpio_dt_spec gpio_ldac;
const struct gpio_dt_spec gpio_busy;
uint8_t latch_mode[MAX22017_MAX_CHANNEL];
uint8_t polarity_mode[MAX22017_MAX_CHANNEL];
uint8_t dac_mode[MAX22017_MAX_CHANNEL];
uint8_t ovc_mode[MAX22017_MAX_CHANNEL];
uint16_t timeout;
};

static int max22017_channel_setup(const struct device *dev,
const struct dac_channel_cfg *channel_cfg)
{
int ret;
uint16_t ao_cnfg, gen_cnfg;
uint8_t chan = channel_cfg->channel_id;
const struct dac_adi_max22017_config *config = dev->config;
const struct device *parent = config->parent;
struct max22017_data *data = parent->data;

if (chan > config->nchannels - 1) {
LOG_ERR("Unsupported channel %d", chan);
return -ENOTSUP;
}

if (channel_cfg->resolution != config->resolution) {
LOG_ERR("Unsupported resolution %d", chan);
return -ENOTSUP;
}

k_mutex_lock(&data->lock, K_FOREVER);
ret = max22017_reg_read(parent, MAX22017_AO_CNFG_OFF, &ao_cnfg);
if (ret) {
goto fail;
}

ao_cnfg |= FIELD_PREP(MAX22017_AO_CNFG_AO_EN, BIT(chan));

if (!config->latch_mode[chan]) {
ao_cnfg |= FIELD_PREP(MAX22017_AO_CNFG_AO_LD_CNFG, BIT(chan));
}

if (config->polarity_mode[chan]) {
ao_cnfg |= FIELD_PREP(MAX22017_AO_CNFG_AO_UNI, BIT(chan));
}

if (config->dac_mode[chan]) {
ao_cnfg |= FIELD_PREP(MAX22017_AO_CNFG_AO_MODE, BIT(chan));
}

ret = max22017_reg_write(parent, MAX22017_AO_CNFG_OFF, ao_cnfg);
if (ret) {
goto fail;
}

ret = max22017_reg_read(parent, MAX22017_GEN_CNFG_OFF, &gen_cnfg);
if (ret) {
goto fail;
}

if (config->ovc_mode[chan]) {
gen_cnfg |= FIELD_PREP(MAX22017_GEN_CNFG_OVC_CNFG, BIT(chan));
/* Over current shutdown mode */
if (config->ovc_mode[chan] == 2) {
gen_cnfg |= FIELD_PREP(MAX22017_GEN_CNFG_OVC_SHDN_CNFG, BIT(chan));
}
}

ret = max22017_reg_write(parent, MAX22017_GEN_CNFG_OFF, gen_cnfg);
fail:
k_mutex_unlock(&data->lock);
return ret;
}

static int max22017_write_value(const struct device *dev, uint8_t channel, uint32_t value)
{
int ret;
uint16_t ao_sta;
const struct dac_adi_max22017_config *config = dev->config;
const struct device *parent = config->parent;
struct max22017_data *data = parent->data;

if (channel > config->nchannels - 1) {
LOG_ERR("unsupported channel %d", channel);
return ENOTSUP;
}

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

k_mutex_lock(&data->lock, K_FOREVER);
if (config->gpio_busy.port) {
if (gpio_pin_get_dt(&config->gpio_busy)) {
ret = -EBUSY;
goto fail;
}
} else {
ret = max22017_reg_read(parent, MAX22017_AO_STA_OFF, &ao_sta);
if (ret) {
goto fail;
}
if (FIELD_GET(MAX22017_AO_STA_BUSY_STA, ao_sta)) {
ret = -EBUSY;
goto fail;
}
}

ret = max22017_reg_write(parent, MAX22017_AO_DATA_CHn_OFF(channel),
FIELD_PREP(MAX22017_AO_DATA_CHn_AO_DATA_CH, value));
if (ret) {
goto fail;
}

if (config->latch_mode[channel]) {
if (config->gpio_ldac.port) {
gpio_pin_set_dt(&config->gpio_ldac, false);
k_sleep(K_USEC(MAX22017_LDAC_TOGGLE_TIME));
gpio_pin_set_dt(&config->gpio_ldac, true);
} else {
ret = max22017_reg_write(
parent, MAX22017_AO_CMD_OFF,
FIELD_PREP(MAX22017_AO_CMD_AO_LD_CTRL, BIT(channel)));
}
}
fail:
k_mutex_unlock(&data->lock);
return ret;
}

static int max22017_init(const struct device *dev)
{
int ret;
uint16_t gen_cnfg = 0, gen_int_en = 0;
const struct dac_adi_max22017_config *config = dev->config;
const struct device *parent = config->parent;
struct max22017_data *data = config->parent->data;

if (!device_is_ready(config->parent)) {
LOG_ERR("parent adi_max22017 MFD device '%s' not ready", config->parent->name);
return -EINVAL;
}

k_mutex_lock(&data->lock, K_FOREVER);

ret = max22017_reg_read(parent, MAX22017_GEN_CNFG_OFF, &gen_cnfg);
if (ret) {
goto fail;
}

ret = max22017_reg_read(parent, MAX22017_GEN_INTEN_OFF, &gen_int_en);
if (ret) {
goto fail;
}

if (config->timeout) {
gen_cnfg |= FIELD_PREP(MAX22017_GEN_CNFG_TMOUT_EN, 1) |
FIELD_PREP(MAX22017_GEN_CNFG_TMOUT_SEL, (config->timeout / 100) - 1);
gen_int_en |= FIELD_PREP(MAX22017_GEN_INTEN_TMOUT_INTEN, 1);
}

ret = max22017_reg_write(parent, MAX22017_GEN_CNFG_OFF, gen_cnfg);
if (ret) {
goto fail;
}

ret = max22017_reg_write(parent, MAX22017_GEN_INTEN_OFF, gen_int_en);
if (ret) {
goto fail;
}

if (config->gpio_ldac.port) {
ret = gpio_pin_configure_dt(&config->gpio_ldac, GPIO_OUTPUT_ACTIVE);
if (ret) {
LOG_ERR("failed to initialize GPIO ldac pin");
goto fail;
}
}

if (config->gpio_busy.port) {
ret = gpio_pin_configure_dt(&config->gpio_busy, GPIO_INPUT);
if (ret) {
LOG_ERR("failed to initialize GPIO busy pin");
goto fail;
}
}

fail:
k_mutex_unlock(&data->lock);
return ret;
}

static const struct dac_driver_api max22017_driver_api = {
.channel_setup = max22017_channel_setup,
.write_value = max22017_write_value,
};

#define DAC_MAX22017_DEVICE(id) \
static const struct dac_adi_max22017_config dac_adi_max22017_config_##id = { \
.parent = DEVICE_DT_GET(DT_INST_PARENT(id)), \
.resolution = DT_INST_PROP_OR(id, resolution, 16), \
.nchannels = DT_INST_PROP_OR(id, num_channels, 2), \
.gpio_busy = GPIO_DT_SPEC_INST_GET_OR(id, busy_gpios, {0}), \
.gpio_ldac = GPIO_DT_SPEC_INST_GET_OR(id, ldac_gpios, {0}), \
.latch_mode = DT_INST_PROP_OR(id, latch_mode, {0}), \
.polarity_mode = DT_INST_PROP_OR(id, polarity_mode, {0}), \
.dac_mode = DT_INST_PROP_OR(id, dac_mode, {0}), \
.ovc_mode = DT_INST_PROP_OR(id, overcurrent_mode, {0}), \
.timeout = DT_INST_PROP_OR(id, timeout, 0), \
}; \
\
DEVICE_DT_INST_DEFINE(id, max22017_init, NULL, NULL, &dac_adi_max22017_config_##id, \
POST_KERNEL, CONFIG_DAC_MAX22017_INIT_PRIORITY, \
&max22017_driver_api);

DT_INST_FOREACH_STATUS_OKAY(DAC_MAX22017_DEVICE);
1 change: 1 addition & 0 deletions drivers/gpio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_LMP90XXX gpio_lmp90xxx.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_LPC11U6X gpio_lpc11u6x.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_MAX14906 gpio_max14906.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_MAX14916 gpio_max14916.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_MAX22017 gpio_max22017.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_MAX32 gpio_max32.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_MCHP_MSS gpio_mchp_mss.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_MCP230XX gpio_mcp230xx.c)
Expand Down
1 change: 1 addition & 0 deletions drivers/gpio/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ source "drivers/gpio/Kconfig.lmp90xxx"
source "drivers/gpio/Kconfig.lpc11u6x"
source "drivers/gpio/Kconfig.max14906"
source "drivers/gpio/Kconfig.max14916"
source "drivers/gpio/Kconfig.max22017"
source "drivers/gpio/Kconfig.max32"
source "drivers/gpio/Kconfig.mchp_mss"
source "drivers/gpio/Kconfig.mcp23xxx"
Expand Down
32 changes: 32 additions & 0 deletions drivers/gpio/Kconfig.max22017
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright (c) 2024 Analog Devices Inc.
# Copyright (c) 2024 BayLibre SAS
# SPDX-License-Identifier: Apache-2.0

config GPIO_MAX22017
bool "Analog Devices MAX22017 GPIO support"
default y
depends on DT_HAS_ADI_MAX22017_GPIO_ENABLED
select MFD
help
Enable GPIO support for the Analog Devices MAX22017

if GPIO_MAX22017

config GPIO_MAX22017_INIT_PRIORITY
int "Init priority"
default 81
help
Analog Devices MAX22017 gpio device driver initialization priority.

config GPIO_MAX22017_INT_QUIRK
bool "MAX22017 GPIO Interrupt quirk"
help
The GPIO controller will not report any new GPI interrupt as long as its interrupt status
register hasn't been read.
Reading the interrupt status register happens on a falling edge of the INT pin.
There seems to be a condition when the GPIO controller detects an interrupt but it's INT
pin stays high which masks any subsequent interrupts.
To avoid being stuck in that state, fire a timer to periodically check the interrupt status
register.

endif # GPIO_MAX22017
Loading
Loading