Skip to content

Commit 31510fb

Browse files
granquetkartben
authored andcommitted
drivers: dac: Add support for MAX22017 DAC
The MAX22017 is a two-channel industrial-grade software-configurable analog output device that can be used in either voltage or current output mode. Signed-off-by: Guillaume Ranquet <[email protected]>
1 parent e297293 commit 31510fb

File tree

6 files changed

+349
-0
lines changed

6 files changed

+349
-0
lines changed

drivers/dac/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ zephyr_library_sources_ifdef(CONFIG_DAC_AD569X dac_ad569x.c)
2525
zephyr_library_sources_ifdef(CONFIG_USERSPACE dac_handlers.c)
2626
zephyr_library_sources_ifdef(CONFIG_DAC_MCUX_GAU dac_mcux_gau.c)
2727
zephyr_library_sources_ifdef(CONFIG_DAC_TEST dac_test.c)
28+
zephyr_library_sources_ifdef(CONFIG_DAC_MAX22017 dac_max22017.c)

drivers/dac/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,6 @@ source "drivers/dac/Kconfig.ad569x"
6161

6262
source "drivers/dac/Kconfig.test"
6363

64+
source "drivers/dac/Kconfig.max22017"
65+
6466
endif # DAC

drivers/dac/Kconfig.max22017

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Copyright (c) 2024 Analog Devices Inc.
2+
# Copyright (c) 2024 BayLibre SAS
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
config DAC_MAX22017
6+
bool "Analog Devices MAX22017 DAC"
7+
default y
8+
depends on DT_HAS_ADI_MAX22017_DAC_ENABLED
9+
select MFD
10+
help
11+
Enable the driver for the Analog Devices MAX22017 DAC
12+
13+
if DAC_MAX22017
14+
15+
config DAC_MAX22017_INIT_PRIORITY
16+
int "Init priority"
17+
default 80
18+
help
19+
Analog Devices MAX22017 DAC device driver initialization priority.
20+
21+
endif # DAC_MAX22017

drivers/dac/dac_max22017.c

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
/*
2+
* Copyright (c) 2024 Analog Devices Inc.
3+
* Copyright (c) 2024 Baylibre SAS
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#include <zephyr/kernel.h>
9+
#include <zephyr/drivers/spi.h>
10+
#include <zephyr/drivers/dac.h>
11+
12+
#include <zephyr/drivers/mfd/max22017.h>
13+
#include <zephyr/logging/log.h>
14+
15+
#define DT_DRV_COMPAT adi_max22017_dac
16+
LOG_MODULE_REGISTER(dac_max22017, CONFIG_DAC_LOG_LEVEL);
17+
18+
struct dac_adi_max22017_config {
19+
const struct device *parent;
20+
uint8_t resolution;
21+
uint8_t nchannels;
22+
const struct gpio_dt_spec gpio_ldac;
23+
const struct gpio_dt_spec gpio_busy;
24+
uint8_t latch_mode[MAX22017_MAX_CHANNEL];
25+
uint8_t polarity_mode[MAX22017_MAX_CHANNEL];
26+
uint8_t dac_mode[MAX22017_MAX_CHANNEL];
27+
uint8_t ovc_mode[MAX22017_MAX_CHANNEL];
28+
uint16_t timeout;
29+
};
30+
31+
static int max22017_channel_setup(const struct device *dev,
32+
const struct dac_channel_cfg *channel_cfg)
33+
{
34+
int ret;
35+
uint16_t ao_cnfg, gen_cnfg;
36+
uint8_t chan = channel_cfg->channel_id;
37+
const struct dac_adi_max22017_config *config = dev->config;
38+
const struct device *parent = config->parent;
39+
struct max22017_data *data = parent->data;
40+
41+
if (chan > config->nchannels - 1) {
42+
LOG_ERR("Unsupported channel %d", chan);
43+
return -ENOTSUP;
44+
}
45+
46+
if (channel_cfg->resolution != config->resolution) {
47+
LOG_ERR("Unsupported resolution %d", chan);
48+
return -ENOTSUP;
49+
}
50+
51+
k_mutex_lock(&data->lock, K_FOREVER);
52+
ret = max22017_reg_read(parent, MAX22017_AO_CNFG_OFF, &ao_cnfg);
53+
if (ret) {
54+
goto fail;
55+
}
56+
57+
ao_cnfg |= FIELD_PREP(MAX22017_AO_CNFG_AO_EN, BIT(chan));
58+
59+
if (!config->latch_mode[chan]) {
60+
ao_cnfg |= FIELD_PREP(MAX22017_AO_CNFG_AO_LD_CNFG, BIT(chan));
61+
}
62+
63+
if (config->polarity_mode[chan]) {
64+
ao_cnfg |= FIELD_PREP(MAX22017_AO_CNFG_AO_UNI, BIT(chan));
65+
}
66+
67+
if (config->dac_mode[chan]) {
68+
ao_cnfg |= FIELD_PREP(MAX22017_AO_CNFG_AO_MODE, BIT(chan));
69+
}
70+
71+
ret = max22017_reg_write(parent, MAX22017_AO_CNFG_OFF, ao_cnfg);
72+
if (ret) {
73+
goto fail;
74+
}
75+
76+
ret = max22017_reg_read(parent, MAX22017_GEN_CNFG_OFF, &gen_cnfg);
77+
if (ret) {
78+
goto fail;
79+
}
80+
81+
if (config->ovc_mode[chan]) {
82+
gen_cnfg |= FIELD_PREP(MAX22017_GEN_CNFG_OVC_CNFG, BIT(chan));
83+
/* Over current shutdown mode */
84+
if (config->ovc_mode[chan] == 2) {
85+
gen_cnfg |= FIELD_PREP(MAX22017_GEN_CNFG_OVC_SHDN_CNFG, BIT(chan));
86+
}
87+
}
88+
89+
ret = max22017_reg_write(parent, MAX22017_GEN_CNFG_OFF, gen_cnfg);
90+
fail:
91+
k_mutex_unlock(&data->lock);
92+
return ret;
93+
}
94+
95+
static int max22017_write_value(const struct device *dev, uint8_t channel, uint32_t value)
96+
{
97+
int ret;
98+
uint16_t ao_sta;
99+
const struct dac_adi_max22017_config *config = dev->config;
100+
const struct device *parent = config->parent;
101+
struct max22017_data *data = parent->data;
102+
103+
if (channel > config->nchannels - 1) {
104+
LOG_ERR("unsupported channel %d", channel);
105+
return ENOTSUP;
106+
}
107+
108+
if (value >= (1 << config->resolution)) {
109+
LOG_ERR("Value %d out of range", value);
110+
return -EINVAL;
111+
}
112+
113+
k_mutex_lock(&data->lock, K_FOREVER);
114+
if (config->gpio_busy.port) {
115+
if (gpio_pin_get_dt(&config->gpio_busy)) {
116+
ret = -EBUSY;
117+
goto fail;
118+
}
119+
} else {
120+
ret = max22017_reg_read(parent, MAX22017_AO_STA_OFF, &ao_sta);
121+
if (ret) {
122+
goto fail;
123+
}
124+
if (FIELD_GET(MAX22017_AO_STA_BUSY_STA, ao_sta)) {
125+
ret = -EBUSY;
126+
goto fail;
127+
}
128+
}
129+
130+
ret = max22017_reg_write(parent, MAX22017_AO_DATA_CHn_OFF(channel),
131+
FIELD_PREP(MAX22017_AO_DATA_CHn_AO_DATA_CH, value));
132+
if (ret) {
133+
goto fail;
134+
}
135+
136+
if (config->latch_mode[channel]) {
137+
if (config->gpio_ldac.port) {
138+
gpio_pin_set_dt(&config->gpio_ldac, false);
139+
k_sleep(K_USEC(MAX22017_LDAC_TOGGLE_TIME));
140+
gpio_pin_set_dt(&config->gpio_ldac, true);
141+
} else {
142+
ret = max22017_reg_write(
143+
parent, MAX22017_AO_CMD_OFF,
144+
FIELD_PREP(MAX22017_AO_CMD_AO_LD_CTRL, BIT(channel)));
145+
}
146+
}
147+
fail:
148+
k_mutex_unlock(&data->lock);
149+
return ret;
150+
}
151+
152+
static int max22017_init(const struct device *dev)
153+
{
154+
int ret;
155+
uint16_t gen_cnfg = 0, gen_int_en = 0;
156+
const struct dac_adi_max22017_config *config = dev->config;
157+
const struct device *parent = config->parent;
158+
struct max22017_data *data = config->parent->data;
159+
160+
if (!device_is_ready(config->parent)) {
161+
LOG_ERR("parent adi_max22017 MFD device '%s' not ready", config->parent->name);
162+
return -EINVAL;
163+
}
164+
165+
k_mutex_lock(&data->lock, K_FOREVER);
166+
167+
ret = max22017_reg_read(parent, MAX22017_GEN_CNFG_OFF, &gen_cnfg);
168+
if (ret) {
169+
goto fail;
170+
}
171+
172+
ret = max22017_reg_read(parent, MAX22017_GEN_INTEN_OFF, &gen_int_en);
173+
if (ret) {
174+
goto fail;
175+
}
176+
177+
if (config->timeout) {
178+
gen_cnfg |= FIELD_PREP(MAX22017_GEN_CNFG_TMOUT_EN, 1) |
179+
FIELD_PREP(MAX22017_GEN_CNFG_TMOUT_SEL, (config->timeout / 100) - 1);
180+
gen_int_en |= FIELD_PREP(MAX22017_GEN_INTEN_TMOUT_INTEN, 1);
181+
}
182+
183+
ret = max22017_reg_write(parent, MAX22017_GEN_CNFG_OFF, gen_cnfg);
184+
if (ret) {
185+
goto fail;
186+
}
187+
188+
ret = max22017_reg_write(parent, MAX22017_GEN_INTEN_OFF, gen_int_en);
189+
if (ret) {
190+
goto fail;
191+
}
192+
193+
if (config->gpio_ldac.port) {
194+
ret = gpio_pin_configure_dt(&config->gpio_ldac, GPIO_OUTPUT_ACTIVE);
195+
if (ret) {
196+
LOG_ERR("failed to initialize GPIO ldac pin");
197+
goto fail;
198+
}
199+
}
200+
201+
if (config->gpio_busy.port) {
202+
ret = gpio_pin_configure_dt(&config->gpio_busy, GPIO_INPUT);
203+
if (ret) {
204+
LOG_ERR("failed to initialize GPIO busy pin");
205+
goto fail;
206+
}
207+
}
208+
209+
fail:
210+
k_mutex_unlock(&data->lock);
211+
return ret;
212+
}
213+
214+
static const struct dac_driver_api max22017_driver_api = {
215+
.channel_setup = max22017_channel_setup,
216+
.write_value = max22017_write_value,
217+
};
218+
219+
#define DAC_MAX22017_DEVICE(id) \
220+
static const struct dac_adi_max22017_config dac_adi_max22017_config_##id = { \
221+
.parent = DEVICE_DT_GET(DT_INST_PARENT(id)), \
222+
.resolution = DT_INST_PROP_OR(id, resolution, 16), \
223+
.nchannels = DT_INST_PROP_OR(id, num_channels, 2), \
224+
.gpio_busy = GPIO_DT_SPEC_INST_GET_OR(id, busy_gpios, {0}), \
225+
.gpio_ldac = GPIO_DT_SPEC_INST_GET_OR(id, ldac_gpios, {0}), \
226+
.latch_mode = DT_INST_PROP_OR(id, latch_mode, {0}), \
227+
.polarity_mode = DT_INST_PROP_OR(id, polarity_mode, {0}), \
228+
.dac_mode = DT_INST_PROP_OR(id, dac_mode, {0}), \
229+
.ovc_mode = DT_INST_PROP_OR(id, overcurrent_mode, {0}), \
230+
.timeout = DT_INST_PROP_OR(id, timeout, 0), \
231+
}; \
232+
\
233+
DEVICE_DT_INST_DEFINE(id, max22017_init, NULL, NULL, &dac_adi_max22017_config_##id, \
234+
POST_KERNEL, CONFIG_DAC_MAX22017_INIT_PRIORITY, \
235+
&max22017_driver_api);
236+
237+
DT_INST_FOREACH_STATUS_OKAY(DAC_MAX22017_DEVICE);

dts/bindings/dac/adi,max22017.yaml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Copyright (c) 2024 Analog Devices Inc.
2+
# Copyright (c) 2024 BayLibre SAS
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
include: dac-controller.yaml
6+
7+
description: Analog Devices MAX22017 16bit DAC
8+
9+
properties:
10+
"#io-channel-cells":
11+
const: 2
12+
13+
num-channels:
14+
type: int
15+
description: Number of DAC output channels.
16+
default: 2
17+
18+
resolution:
19+
type: int
20+
description: DAC resolution.
21+
default: 16
22+
23+
busy-gpios:
24+
description: Busy line indicating the DAC is calculating next sample.
25+
type: phandle-array
26+
27+
ldac-gpios:
28+
description: Load both DAC latches at the same time.
29+
type: phandle-array
30+
31+
polarity-mode:
32+
description: |
33+
Unipolar/bipolar mode selection for channels.
34+
0 Indicates bipolar mode and 1 unipolar mode.
35+
The default settings to bipolar here align with the default mode of the device.
36+
default: [0, 0]
37+
type: uint8-array
38+
39+
dac-mode:
40+
description: |
41+
Voltage/current mode selection for channels.
42+
0 indicates voltage mode and 1 indicates current mode.
43+
The default settings to voltage mode here align with the default mode of the device.
44+
default: [0, 0]
45+
type: uint8-array
46+
47+
latch-mode:
48+
description: |
49+
Latch mode selection for channels.
50+
0 means the channel is not latched, 1 means latched.
51+
The default settings to non latched should be more straightforward to use than the latched
52+
mode. The latch mode can be used eitheir with the ldac-gpios to load both channels at the
53+
same time or if no ldac-gpios property is set, latching will be done per channel with a
54+
register write.
55+
default: [0, 0]
56+
type: uint8-array
57+
58+
overcurrent-mode:
59+
description: |
60+
Overcurrent mode selection for channels.
61+
0 for current limiting mode
62+
1 for short circuit protection auto power up mode
63+
2 for short circuit protection shutdown mode
64+
The default setting to current limiting mode here aligns with the default mode of the device.
65+
default: [0, 0]
66+
type: uint8-array
67+
68+
timeout:
69+
description: |
70+
Timeout in ms.
71+
The value should be between 100 and 1600ms in increments of 100ms.
72+
type: int
73+
74+
compatible: "adi,max22017-dac"

tests/drivers/build_all/dac/app.overlay

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
<&test_gpio 0 0>,
125125
<&test_gpio 0 0>,
126126
<&test_gpio 0 0>,
127+
<&test_gpio 0 0>,
127128
<&test_gpio 0 0>;
128129

129130
test_spi_dac60508: dac60508@0 {
@@ -288,6 +289,19 @@
288289
#io-channel-cells = <1>;
289290
};
290291
};
292+
293+
test_spi_max22017_mfd: max22017_mfd@11 {
294+
compatible = "adi,max22017";
295+
status = "okay";
296+
spi-max-frequency = <1000000>;
297+
reg = <0x11>;
298+
299+
test_spi_max22017_dac0: dac-controller {
300+
compatible = "adi,max22017-dac";
301+
#io-channel-cells = <2>;
302+
status = "okay";
303+
};
304+
};
291305
};
292306
};
293307
};

0 commit comments

Comments
 (0)