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