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