Skip to content

Commit ef1e954

Browse files
jpfaffgalak
authored andcommitted
drivers: adc: Add SAM ADC driver
Tested on Atmel SMART SAM E70 Xplained board Origin: Original Jira: ZEP-2507 Signed-off-by: Jonas Pfaff <[email protected]>
1 parent ac758cf commit ef1e954

File tree

9 files changed

+351
-0
lines changed

9 files changed

+351
-0
lines changed

arch/arm/soc/atmel_sam/same70/soc_pinmap.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515

1616
#include <soc.h>
1717

18+
/* Analog to Digital Converter (AFEC) */
19+
20+
#define PIN_AFE0_ADTRG {PIO_PA8B_AFEC0_ADTRG, PIOA, ID_PIOA, SOC_GPIO_FUNC_B}
21+
#define PIN_AFE1_ADTRG {PIO_PD9C_AFEC1_ADTRG, PIOD, ID_PIOD, SOC_GPIO_FUNC_C}
22+
1823
/* Ethernet MAC (GMAC) */
1924

2025
#define PINS_GMAC_MASK (PIO_PD0A_GMAC_GTXCK | PIO_PD1A_GMAC_GTXEN \

drivers/adc/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ source "drivers/adc/Kconfig.mcux"
8080

8181
source "drivers/adc/Kconfig.qmsi"
8282

83+
source "drivers/adc/Kconfig.sam_afec"
84+
8385
source "drivers/adc/Kconfig.ti_adc108s102"
8486

8587
endif # ADC

drivers/adc/Kconfig.sam_afec

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Kconfig - ADC configuration options
2+
3+
#
4+
# Copyright (c) 2017 comsuisse AG
5+
#
6+
# SPDX-License-Identifier: Apache-2.0
7+
#
8+
9+
menuconfig ADC_SAM_AFEC
10+
bool "SAM ADC Driver"
11+
depends on SOC_FAMILY_SAM
12+
select HAS_DTS_ADC
13+
default n
14+
help
15+
Enable Atmel SAM MCU Family Analog-to-Digital Converter (ADC) driver
16+
based on AFEC module.

drivers/adc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ obj-$(CONFIG_ADC_MCUX_ADC16) += adc_mcux_adc16.o
33
obj-$(CONFIG_ADC_TI_ADC108S102) += adc_ti_adc108s102.o
44
obj-$(CONFIG_ADC_QMSI) += adc_qmsi.o
55
obj-$(CONFIG_ADC_QMSI_SS) += adc_qmsi_ss.o
6+
obj-$(CONFIG_ADC_SAM_AFEC) += adc_sam_afec.o

drivers/adc/adc_sam_afec.c

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
/*
2+
* Copyright (c) 2017 comsuisse AG
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/** @file
8+
* @brief Atmel SAM MCU family ADC (AFEC) driver.
9+
*/
10+
11+
#include <errno.h>
12+
#include <misc/__assert.h>
13+
#include <misc/util.h>
14+
#include <device.h>
15+
#include <init.h>
16+
#include <soc.h>
17+
#include <adc.h>
18+
19+
#define SYS_LOG_DOMAIN "dev/adc_sam_afec"
20+
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_ADC_LEVEL
21+
#include <logging/sys_log.h>
22+
23+
#define ADC_CHANNELS 12
24+
25+
struct adc_sam_dev_cfg {
26+
Afec *regs;
27+
const struct soc_gpio_pin *pin_trigger;
28+
void (*irq_config)(void);
29+
u8_t irq_id;
30+
u8_t periph_id;
31+
};
32+
33+
struct channel_samples {
34+
u16_t *buffer;
35+
u32_t length;
36+
};
37+
38+
struct adc_sam_dev_data {
39+
struct k_sem sem_meas;
40+
struct k_sem mutex_thread;
41+
u16_t active_channels;
42+
u16_t measured_channels;
43+
u16_t active_chan_last;
44+
struct channel_samples samples[ADC_CHANNELS];
45+
};
46+
47+
#define CONF_ADC_PRESCALER ((SOC_ATMEL_SAM_MCK_FREQ_HZ / 15000000) - 1)
48+
49+
#define DEV_NAME(dev) ((dev)->config->name)
50+
#define DEV_CFG(dev) \
51+
((const struct adc_sam_dev_cfg *const)(dev)->config->config_info)
52+
#define DEV_DATA(dev) \
53+
((struct adc_sam_dev_data *const)(dev)->driver_data)
54+
55+
static void adc_sam_isr(void *arg)
56+
{
57+
struct device *dev = (struct device *)arg;
58+
const struct adc_sam_dev_cfg *const dev_cfg = DEV_CFG(dev);
59+
struct adc_sam_dev_data *const dev_data = DEV_DATA(dev);
60+
Afec *const afec = dev_cfg->regs;
61+
struct channel_samples *samples = dev_data->samples;
62+
u32_t isr_status;
63+
u16_t value;
64+
65+
isr_status = afec->AFEC_ISR;
66+
for (int i = 0; i < ADC_CHANNELS; i++) {
67+
if (isr_status & BIT(i)) {
68+
/* Channel i has been sampled */
69+
afec->AFEC_CSELR = AFEC_CSELR_CSEL(i);
70+
value = (u16_t)(afec->AFEC_CDR);
71+
72+
if (samples[i].length > 0) {
73+
/* Save sample */
74+
*samples[i].buffer = value;
75+
samples[i].length--;
76+
samples[i].buffer++;
77+
78+
dev_data->measured_channels |= BIT(i);
79+
}
80+
if (samples[i].length == 0) {
81+
afec->AFEC_CHDR |= BIT(i);
82+
afec->AFEC_IDR |= BIT(i);
83+
dev_data->active_channels &= ~BIT(i);
84+
}
85+
}
86+
}
87+
88+
if (dev_data->active_chan_last == dev_data->measured_channels) {
89+
if (!dev_data->active_channels) {
90+
/* No more conversions needed */
91+
k_sem_give(&dev_data->sem_meas);
92+
} else {
93+
/* Start another conversion with remaining channels */
94+
dev_data->measured_channels = 0;
95+
dev_data->active_chan_last = dev_data->active_channels;
96+
afec->AFEC_CR = AFEC_CR_START;
97+
}
98+
}
99+
}
100+
101+
static int adc_sam_read(struct device *dev, struct adc_seq_table *seq_tbl)
102+
{
103+
const struct adc_sam_dev_cfg *dev_cfg = DEV_CFG(dev);
104+
struct adc_sam_dev_data *const dev_data = DEV_DATA(dev);
105+
Afec *const afec = dev_cfg->regs;
106+
u8_t channel;
107+
u32_t num_samples;
108+
109+
k_sem_take(&dev_data->mutex_thread, K_FOREVER);
110+
111+
dev_data->active_channels = 0;
112+
113+
/* Enable chosen channels */
114+
for (int i = 0; i < seq_tbl->num_entries; i++) {
115+
channel = seq_tbl->entries[i].channel_id;
116+
if (channel >= ADC_CHANNELS) {
117+
return -EINVAL;
118+
}
119+
120+
/* Check and set number of requested samples */
121+
num_samples = seq_tbl->entries[i].buffer_length / sizeof(u16_t);
122+
if (!num_samples) {
123+
return -EINVAL;
124+
}
125+
dev_data->samples[channel].length = num_samples;
126+
127+
/* Set start of sample buffer */
128+
dev_data->samples[channel].buffer =
129+
(u16_t *)seq_tbl->entries[i].buffer;
130+
131+
/* Enable channel */
132+
dev_data->active_channels |= BIT(channel);
133+
}
134+
135+
/* Enable chosen channels and their interrupts */
136+
afec->AFEC_CHER = dev_data->active_channels;
137+
afec->AFEC_IER = dev_data->active_channels;
138+
139+
/* Start conversions */
140+
dev_data->measured_channels = 0;
141+
dev_data->active_chan_last = dev_data->active_channels;
142+
afec->AFEC_CR = AFEC_CR_START;
143+
k_sem_take(&dev_data->sem_meas, K_FOREVER);
144+
145+
k_sem_give(&dev_data->mutex_thread);
146+
147+
return 0;
148+
}
149+
150+
static void adc_sam_configure(struct device *dev)
151+
{
152+
const struct adc_sam_dev_cfg *dev_cfg = DEV_CFG(dev);
153+
Afec *const afec = dev_cfg->regs;
154+
155+
/* Reset the AFEC */
156+
afec->AFEC_CR = AFEC_CR_SWRST;
157+
158+
afec->AFEC_MR = AFEC_MR_TRGEN_DIS
159+
| AFEC_MR_SLEEP_NORMAL
160+
| AFEC_MR_FWUP_OFF
161+
| AFEC_MR_FREERUN_OFF
162+
| AFEC_MR_PRESCAL(CONF_ADC_PRESCALER)
163+
| AFEC_MR_STARTUP_SUT96
164+
| AFEC_MR_ONE
165+
| AFEC_MR_USEQ_NUM_ORDER;
166+
167+
/* Set all channels CM voltage to Vrefp/2 (512) */
168+
for (int i = 0; i < ADC_CHANNELS; i++) {
169+
afec->AFEC_CSELR = i;
170+
afec->AFEC_COCR = 512;
171+
}
172+
173+
/* Enable PGA and Current Bias */
174+
afec->AFEC_ACR = AFEC_ACR_PGA0EN
175+
| AFEC_ACR_PGA1EN
176+
| AFEC_ACR_IBCTL(1);
177+
}
178+
179+
static int adc_sam_initialize(struct device *dev)
180+
{
181+
const struct adc_sam_dev_cfg *dev_cfg = DEV_CFG(dev);
182+
struct adc_sam_dev_data *const dev_data = DEV_DATA(dev);
183+
184+
/* Initialize semaphores */
185+
k_sem_init(&dev_data->sem_meas, 0, 1);
186+
k_sem_init(&dev_data->mutex_thread, 1, 1);
187+
188+
/* Configure interrupts */
189+
dev_cfg->irq_config();
190+
191+
/* Enable module's clock */
192+
soc_pmc_peripheral_enable(dev_cfg->periph_id);
193+
194+
/* Configure ADC */
195+
adc_sam_configure(dev);
196+
197+
/* Enable module interrupt */
198+
irq_enable(dev_cfg->irq_id);
199+
200+
SYS_LOG_INF("Device %s initialized", DEV_NAME(dev));
201+
202+
return 0;
203+
}
204+
205+
static const struct adc_driver_api adc_sam_driver_api = {
206+
.read = adc_sam_read,
207+
};
208+
209+
/* ADC_0 */
210+
211+
#ifdef CONFIG_ADC_0
212+
static struct device DEVICE_NAME_GET(adc0_sam);
213+
214+
static void adc0_irq_config(void)
215+
{
216+
IRQ_CONNECT(AFEC0_IRQn, CONFIG_ADC_0_IRQ_PRI, adc_sam_isr,
217+
DEVICE_GET(adc0_sam), 0);
218+
}
219+
220+
static const struct soc_gpio_pin pin_adc0 = PIN_AFE0_ADTRG;
221+
222+
static const struct adc_sam_dev_cfg adc0_sam_config = {
223+
.regs = AFEC0,
224+
.pin_trigger = &pin_adc0,
225+
.periph_id = ID_AFEC0,
226+
.irq_config = adc0_irq_config,
227+
.irq_id = AFEC0_IRQn,
228+
};
229+
230+
static struct adc_sam_dev_data adc0_sam_data;
231+
232+
DEVICE_AND_API_INIT(adc0_sam, CONFIG_ADC_0_NAME, &adc_sam_initialize,
233+
&adc0_sam_data, &adc0_sam_config, POST_KERNEL,
234+
CONFIG_ADC_INIT_PRIORITY, &adc_sam_driver_api);
235+
#endif
236+
237+
/* ADC_1 */
238+
239+
#ifdef CONFIG_ADC_1
240+
static struct device DEVICE_NAME_GET(adc1_sam);
241+
242+
static void adc1_irq_config(void)
243+
{
244+
IRQ_CONNECT(AFEC1_IRQn, CONFIG_ADC_1_IRQ_PRI, adc_sam_isr,
245+
DEVICE_GET(adc1_sam), 0);
246+
}
247+
248+
static const struct soc_gpio_pin pin_adc1 = PIN_AFE1_ADTRG;
249+
250+
static const struct adc_sam_dev_cfg adc1_sam_config = {
251+
.regs = AFEC1,
252+
.pin_trigger = &pin_adc1,
253+
.periph_id = ID_AFEC1,
254+
.irq_config = adc1_irq_config,
255+
.irq_id = AFEC1_IRQn,
256+
};
257+
258+
static struct adc_sam_dev_data adc1_sam_data;
259+
260+
DEVICE_AND_API_INIT(adc1_sam, CONFIG_ADC_1_NAME, &adc_sam_initialize,
261+
&adc1_sam_data, &adc1_sam_config, POST_KERNEL,
262+
CONFIG_ADC_INIT_PRIORITY, &adc_sam_driver_api);
263+
#endif

dts/arm/atmel/same70.dtsi

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,22 @@
9494
status = "disabled";
9595
label = "USART_2";
9696
};
97+
98+
adc0: adc@4003C000 {
99+
compatible = "atmel,sam-afec";
100+
reg = <0x4003C000 0x100>;
101+
interrupts = <29 0>;
102+
status = "disabled";
103+
label = "ADC_0";
104+
};
105+
106+
adc1: adc@40064000 {
107+
compatible = "atmel,sam-afec";
108+
reg = <0x40064000 0x100>;
109+
interrupts = <40 0>;
110+
status = "disabled";
111+
label = "ADC_1";
112+
};
97113
};
98114
};
99115

dts/arm/sam_e70_xplained.dts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@
2020
};
2121
};
2222

23+
&adc0 {
24+
status = "ok";
25+
};
26+
27+
&adc1 {
28+
status = "ok";
29+
};
30+
2331
&usart1 {
2432
current-speed = <115200>;
2533
status = "ok";

dts/arm/sam_e70_xplained.fixup

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,13 @@
2222
#define CONFIG_USART_SAM_PORT_1_BAUD_RATE ATMEL_SAM_USART_40028000_CURRENT_SPEED
2323
#define CONFIG_USART_SAM_PORT_2_NAME ATMEL_SAM_USART_4002C000_LABEL
2424
#define CONFIG_USART_SAM_PORT_2_BAUD_RATE ATMEL_SAM_USART_4002C000_CURRENT_SPEED
25+
26+
#define CONFIG_ADC_0_BASE_ADDRESS ATMEL_SAM_AFEC_4003C000_BASE_ADDRESS_0
27+
#define CONFIG_ADC_0_IRQ ATMEL_SAM_AFEC_4003C000_IRQ_0
28+
#define CONFIG_ADC_0_IRQ_PRI ATMEL_SAM_AFEC_4003C000_IRQ_0_PRIORITY
29+
#define CONFIG_ADC_0_NAME ATMEL_SAM_AFEC_4003C000_LABEL
30+
31+
#define CONFIG_ADC_1_BASE_ADDRESS ATMEL_SAM_AFEC_40064000_BASE_ADDRESS_0
32+
#define CONFIG_ADC_1_IRQ ATMEL_SAM_AFEC_40064000_IRQ_0
33+
#define CONFIG_ADC_1_IRQ_PRI ATMEL_SAM_AFEC_40064000_IRQ_0_PRIORITY
34+
#define CONFIG_ADC_1_NAME ATMEL_SAM_AFEC_40064000_LABEL

dts/arm/yaml/atmel,sam-afec.yaml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
title: Atmel SAM Family AFEC
3+
id: atmel,sam-afec
4+
version: 0.1
5+
6+
description: >
7+
This binding gives a base representation of the Atmel SAM AFEC
8+
9+
inherits:
10+
- !include adc.yaml
11+
12+
properties:
13+
- compatible:
14+
type: string
15+
category: required
16+
description: compatible strings
17+
constraint: "atmel,sam-afec"
18+
19+
- reg:
20+
type: array
21+
description: mmio register space
22+
generation: define
23+
category: required
24+
25+
- interrupts:
26+
type: array
27+
category: required
28+
description: required interrupts
29+
generation: define
30+
...

0 commit comments

Comments
 (0)