1 /*
2  * Copyright (c) 2021 Piotr Mienkowski
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 /** @file
7  * @brief DAC driver for Atmel SAM MCU family.
8  */
9 
10 #define DT_DRV_COMPAT atmel_sam_dac
11 
12 #include <errno.h>
13 #include <zephyr/sys/__assert.h>
14 #include <soc.h>
15 #include <zephyr/device.h>
16 #include <zephyr/drivers/dac.h>
17 #include <zephyr/drivers/pinctrl.h>
18 #include <zephyr/drivers/clock_control/atmel_sam_pmc.h>
19 #include <zephyr/kernel.h>
20 #include <zephyr/logging/log.h>
21 #include <zephyr/irq.h>
22 LOG_MODULE_REGISTER(dac_sam, CONFIG_DAC_LOG_LEVEL);
23 
24 #define DAC_CHANNEL_NO  2
25 
26 /* Device constant configuration parameters */
27 struct dac_sam_dev_cfg {
28 	Dacc *regs;
29 	const struct atmel_sam_pmc_config clock_cfg;
30 	const struct pinctrl_dev_config *pcfg;
31 	void (*irq_config)(void);
32 	uint8_t irq_id;
33 	uint8_t prescaler;
34 };
35 
36 struct dac_channel {
37 	struct k_sem sem;
38 };
39 
40 /* Device run time data */
41 struct dac_sam_dev_data {
42 #if defined(CONFIG_SOC_SERIES_SAMX7X)
43 	struct dac_channel dac_channels[DAC_CHANNEL_NO];
44 #else
45 	struct dac_channel dac_channel;
46 #endif
47 };
48 
dac_sam_isr(const struct device * dev)49 static void dac_sam_isr(const struct device *dev)
50 {
51 	const struct dac_sam_dev_cfg *const dev_cfg = dev->config;
52 	struct dac_sam_dev_data *const dev_data = dev->data;
53 	Dacc *const dac = dev_cfg->regs;
54 	uint32_t int_stat;
55 
56 	/* Retrieve interrupt status */
57 	int_stat = dac->DACC_ISR & dac->DACC_IMR;
58 
59 #if defined(CONFIG_SOC_SERIES_SAMX7X)
60 	if ((int_stat & DACC_ISR_TXRDY0) != 0) {
61 		/* Disable Transmit Ready Interrupt */
62 		dac->DACC_IDR = DACC_IDR_TXRDY0;
63 		k_sem_give(&dev_data->dac_channels[0].sem);
64 	}
65 	if ((int_stat & DACC_ISR_TXRDY1) != 0) {
66 		/* Disable Transmit Ready Interrupt */
67 		dac->DACC_IDR = DACC_IDR_TXRDY1;
68 		k_sem_give(&dev_data->dac_channels[1].sem);
69 	}
70 #else
71 	if ((int_stat & DACC_ISR_TXRDY) != 0) {
72 		/* Disable Transmit Ready Interrupt */
73 		dac->DACC_IDR = DACC_IDR_TXRDY;
74 		k_sem_give(&dev_data->dac_channel.sem);
75 	}
76 #endif
77 }
78 
dac_sam_channel_setup(const struct device * dev,const struct dac_channel_cfg * channel_cfg)79 static int dac_sam_channel_setup(const struct device *dev,
80 				 const struct dac_channel_cfg *channel_cfg)
81 {
82 	const struct dac_sam_dev_cfg *const dev_cfg = dev->config;
83 	Dacc *const dac = dev_cfg->regs;
84 
85 	if (channel_cfg->channel_id >= DAC_CHANNEL_NO) {
86 		return -EINVAL;
87 	}
88 	if (channel_cfg->resolution != 12) {
89 		return -ENOTSUP;
90 	}
91 
92 	if (channel_cfg->internal) {
93 		return -ENOTSUP;
94 	}
95 
96 	/* Enable Channel */
97 	dac->DACC_CHER = DACC_CHER_CH0 << channel_cfg->channel_id;
98 
99 	return 0;
100 }
101 
dac_sam_write_value(const struct device * dev,uint8_t channel,uint32_t value)102 static int dac_sam_write_value(const struct device *dev, uint8_t channel,
103 			       uint32_t value)
104 {
105 	struct dac_sam_dev_data *const dev_data = dev->data;
106 	const struct dac_sam_dev_cfg *const dev_cfg = dev->config;
107 	Dacc *const dac = dev_cfg->regs;
108 
109 	if (channel >= DAC_CHANNEL_NO) {
110 		return -EINVAL;
111 	}
112 
113 #if defined(CONFIG_SOC_SERIES_SAMX7X)
114 	if (dac->DACC_IMR & (DACC_IMR_TXRDY0 << channel)) {
115 #else
116 	if (dac->DACC_IMR & DACC_IMR_TXRDY) {
117 #endif
118 		/* Attempting to send data on channel that's already in use */
119 		return -EINVAL;
120 	}
121 
122 	if (value >= BIT(12)) {
123 		LOG_ERR("value %d out of range", value);
124 		return -EINVAL;
125 	}
126 
127 #if defined(CONFIG_SOC_SERIES_SAMX7X)
128 	k_sem_take(&dev_data->dac_channels[channel].sem, K_FOREVER);
129 
130 	/* Trigger conversion */
131 	dac->DACC_CDR[channel] = DACC_CDR_DATA0(value);
132 
133 	/* Enable Transmit Ready Interrupt */
134 	dac->DACC_IER = DACC_IER_TXRDY0 << channel;
135 #else
136 	k_sem_take(&dev_data->dac_channel.sem, K_FOREVER);
137 
138 	/* Select the channel */
139 	dac->DACC_MR = DACC_MR_USER_SEL(channel) | DACC_MR_ONE;
140 
141 	/* Trigger conversion */
142 	dac->DACC_CDR = DACC_CDR_DATA(value);
143 
144 	/* Enable Transmit Ready Interrupt */
145 	dac->DACC_IER = DACC_IER_TXRDY;
146 #endif
147 
148 	return 0;
149 }
150 
151 static int dac_sam_init(const struct device *dev)
152 {
153 	const struct dac_sam_dev_cfg *const dev_cfg = dev->config;
154 	struct dac_sam_dev_data *const dev_data = dev->data;
155 	int retval;
156 #if defined(CONFIG_SOC_SERIES_SAMX7X)
157 	Dacc *const dac = dev_cfg->regs;
158 #endif
159 
160 	/* Configure interrupts */
161 	dev_cfg->irq_config();
162 
163 	/* Initialize semaphores */
164 #if defined(CONFIG_SOC_SERIES_SAMX7X)
165 	for (int i = 0; i < ARRAY_SIZE(dev_data->dac_channels); i++) {
166 		k_sem_init(&dev_data->dac_channels[i].sem, 1, 1);
167 	}
168 #else
169 	k_sem_init(&dev_data->dac_channel.sem, 1, 1);
170 #endif
171 
172 	/* Enable DAC clock in PMC */
173 	(void)clock_control_on(SAM_DT_PMC_CONTROLLER,
174 			       (clock_control_subsys_t)&dev_cfg->clock_cfg);
175 
176 	retval = pinctrl_apply_state(dev_cfg->pcfg, PINCTRL_STATE_DEFAULT);
177 	if (retval < 0) {
178 		return retval;
179 	}
180 
181 #if defined(CONFIG_SOC_SERIES_SAMX7X)
182 	/* Set Mode Register */
183 	dac->DACC_MR = DACC_MR_PRESCALER(dev_cfg->prescaler);
184 #endif
185 
186 	/* Enable module's IRQ */
187 	irq_enable(dev_cfg->irq_id);
188 
189 	LOG_INF("Device %s initialized", dev->name);
190 
191 	return 0;
192 }
193 
194 static DEVICE_API(dac, dac_sam_driver_api) = {
195 	.channel_setup = dac_sam_channel_setup,
196 	.write_value = dac_sam_write_value,
197 };
198 
199 /* DACC */
200 
201 static void dacc_irq_config(void)
202 {
203 	IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), dac_sam_isr,
204 		    DEVICE_DT_INST_GET(0), 0);
205 }
206 
207 PINCTRL_DT_INST_DEFINE(0);
208 
209 static const struct dac_sam_dev_cfg dacc_sam_config = {
210 	.regs = (Dacc *)DT_INST_REG_ADDR(0),
211 	.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0),
212 	.irq_id = DT_INST_IRQN(0),
213 	.irq_config = dacc_irq_config,
214 	.clock_cfg = SAM_DT_INST_CLOCK_PMC_CFG(0),
215 	.prescaler = DT_INST_PROP(0, prescaler),
216 };
217 
218 static struct dac_sam_dev_data dacc_sam_data;
219 
220 DEVICE_DT_INST_DEFINE(0, dac_sam_init, NULL, &dacc_sam_data, &dacc_sam_config,
221 		      POST_KERNEL, CONFIG_DAC_INIT_PRIORITY,
222 		      &dac_sam_driver_api);
223