1 /*
2 * Copyright (c) 2020 Google LLC.
3 * Copyright (c) 2024 Gerson Fernando Budke <nandojve@gmail.com>
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #define DT_DRV_COMPAT atmel_sam0_dac
9
10 #include <errno.h>
11
12 #include <zephyr/drivers/dac.h>
13 #include <zephyr/drivers/pinctrl.h>
14 #include <soc.h>
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(dac_sam0, CONFIG_DAC_LOG_LEVEL);
17
18 /* clang-format off */
19
20 /*
21 * Maps between the DTS reference property names and register values. Note that
22 * the ASF uses the 09/2015 names which differ from the 03/2020 datasheet.
23 *
24 * TODO(#21273): replace once improved support for enum values lands.
25 */
26 #define SAM0_DAC_REFSEL_0 DAC_CTRLB_REFSEL_INT1V_Val
27 #define SAM0_DAC_REFSEL_1 DAC_CTRLB_REFSEL_AVCC_Val
28 #define SAM0_DAC_REFSEL_2 DAC_CTRLB_REFSEL_VREFP_Val
29
30 struct dac_sam0_cfg {
31 Dac *regs;
32 const struct pinctrl_dev_config *pcfg;
33 volatile uint32_t *mclk;
34 uint32_t mclk_mask;
35 uint32_t gclk_gen;
36 uint16_t gclk_id;
37 uint8_t refsel;
38 };
39
40 /* Write to the DAC. */
dac_sam0_write_value(const struct device * dev,uint8_t channel,uint32_t value)41 static int dac_sam0_write_value(const struct device *dev, uint8_t channel,
42 uint32_t value)
43 {
44 const struct dac_sam0_cfg *const cfg = dev->config;
45 Dac *regs = cfg->regs;
46
47 if (value >= BIT(12)) {
48 LOG_ERR("value %d out of range", value);
49 return -EINVAL;
50 }
51
52 regs->DATA.reg = (uint16_t)value;
53
54 return 0;
55 }
56
57 /*
58 * Setup the channel. As the SAM0 has one fixed width channel, this validates
59 * the input and does nothing else.
60 */
dac_sam0_channel_setup(const struct device * dev,const struct dac_channel_cfg * channel_cfg)61 static int dac_sam0_channel_setup(const struct device *dev,
62 const struct dac_channel_cfg *channel_cfg)
63 {
64 if (channel_cfg->channel_id != 0) {
65 return -EINVAL;
66 }
67 if (channel_cfg->resolution != 10) {
68 return -ENOTSUP;
69 }
70
71 if (channel_cfg->internal) {
72 return -ENOSYS;
73 }
74
75 return 0;
76 }
77
78 /* Initialise and enable the DAC. */
dac_sam0_init(const struct device * dev)79 static int dac_sam0_init(const struct device *dev)
80 {
81 const struct dac_sam0_cfg *const cfg = dev->config;
82 Dac *regs = cfg->regs;
83 int retval;
84
85 *cfg->mclk |= cfg->mclk_mask;
86
87 #ifdef MCLK
88 GCLK->PCHCTRL[cfg->gclk_id].reg = GCLK_PCHCTRL_CHEN
89 | GCLK_PCHCTRL_GEN(cfg->gclk_gen);
90 #else
91 GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN
92 | GCLK_CLKCTRL_GEN(cfg->gclk_gen)
93 | GCLK_CLKCTRL_ID(cfg->gclk_id);
94 #endif
95
96 retval = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
97 if (retval < 0) {
98 return retval;
99 }
100
101 /* Reset then configure the DAC */
102 regs->CTRLA.bit.SWRST = 1;
103 while (regs->STATUS.bit.SYNCBUSY) {
104 }
105
106 regs->CTRLB.bit.REFSEL = cfg->refsel;
107 regs->CTRLB.bit.EOEN = 1;
108
109 /* Enable */
110 regs->CTRLA.bit.ENABLE = 1;
111 while (regs->STATUS.bit.SYNCBUSY) {
112 }
113
114 return 0;
115 }
116
117 static DEVICE_API(dac, api_sam0_driver_api) = {
118 .channel_setup = dac_sam0_channel_setup,
119 .write_value = dac_sam0_write_value
120 };
121
122 #define ASSIGNED_CLOCKS_CELL_BY_NAME \
123 ATMEL_SAM0_DT_INST_ASSIGNED_CLOCKS_CELL_BY_NAME
124
125 #define SAM0_DAC_REFSEL(n) \
126 COND_CODE_1(DT_INST_NODE_HAS_PROP(n, reference), \
127 (DT_INST_ENUM_IDX(n, reference)), (0))
128
129 #define SAM0_DAC_INIT(n) \
130 PINCTRL_DT_INST_DEFINE(n); \
131 static const struct dac_sam0_cfg dac_sam0_cfg_##n = { \
132 .regs = (Dac *)DT_INST_REG_ADDR(n), \
133 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
134 .gclk_gen = ASSIGNED_CLOCKS_CELL_BY_NAME(n, gclk, gen), \
135 .gclk_id = DT_INST_CLOCKS_CELL_BY_NAME(n, gclk, id), \
136 .mclk = ATMEL_SAM0_DT_INST_MCLK_PM_REG_ADDR_OFFSET(n), \
137 .mclk_mask = ATMEL_SAM0_DT_INST_MCLK_PM_PERIPH_MASK(n, bit), \
138 .refsel = UTIL_CAT(SAM0_DAC_REFSEL_, SAM0_DAC_REFSEL(n)), \
139 }; \
140 \
141 DEVICE_DT_INST_DEFINE(n, &dac_sam0_init, NULL, NULL, \
142 &dac_sam0_cfg_##n, POST_KERNEL, \
143 CONFIG_DAC_INIT_PRIORITY, \
144 &api_sam0_driver_api)
145
146 DT_INST_FOREACH_STATUS_OKAY(SAM0_DAC_INIT);
147
148 /* clang-format on */
149