1 /*
2 * Copyright (c) 2016 Piotr Mienkowski
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 /** @file
7 * @brief Atmel SAM MCU family General Purpose Input Output (GPIO)
8 * module HAL driver.
9 */
10
11 #include <zephyr/sys/__assert.h>
12 #include "soc_gpio.h"
13
14 /*
15 * There exist minor differences between SAM MCU family members in naming
16 * of some of the registers. Check that our expectations are met.
17 */
18 #if (!defined(PIO_IFSCER_P0) && !defined(PIO_DIFSR_P0)) \
19 || (!defined(PIO_IFSCDR_P0) && !defined(PIO_SCIFSR_P0)) \
20 || (!defined(PIO_ABCDSR_P0) && !defined(PIO_ABSR_P0))
21 #error "Unsupported Atmel SAM MCU series"
22 #endif
23
configure_common_attr(Pio * pio,uint32_t mask,uint32_t flags)24 static void configure_common_attr(Pio *pio, uint32_t mask, uint32_t flags)
25 {
26 /* Disable interrupts on the pin(s) */
27 pio->PIO_IDR = mask;
28
29 /* Configure pull-up(s) */
30 if (flags & SOC_GPIO_PULLUP) {
31 pio->PIO_PUER = mask;
32 } else {
33 pio->PIO_PUDR = mask;
34 }
35
36 /* Configure pull-down only for MCU series that support it */
37 #if defined PIO_PPDER_P0
38 /* Configure pull-down(s) */
39 if (flags & SOC_GPIO_PULLDOWN) {
40 pio->PIO_PPDER = mask;
41 } else {
42 pio->PIO_PPDDR = mask;
43 }
44 #endif
45
46 /* Configure open drain (multi-drive) */
47 if (flags & SOC_GPIO_OPENDRAIN) {
48 pio->PIO_MDER = mask;
49 } else {
50 pio->PIO_MDDR = mask;
51 }
52 }
53
configure_input_attr(Pio * pio,uint32_t mask,uint32_t flags)54 static void configure_input_attr(Pio *pio, uint32_t mask, uint32_t flags)
55 {
56 /* Configure input filter */
57 if ((flags & SOC_GPIO_IN_FILTER_MASK) != 0U) {
58 if ((flags & SOC_GPIO_IN_FILTER_MASK) == SOC_GPIO_IN_FILTER_DEBOUNCE) {
59 /* Enable de-bounce, disable de-glitch */
60 #if defined PIO_IFSCER_P0
61 pio->PIO_IFSCER = mask;
62 #elif defined PIO_DIFSR_P0
63 pio->PIO_DIFSR = mask;
64 #endif
65 } else {
66 /* Disable de-bounce, enable de-glitch */
67 #if defined PIO_IFSCDR_P0
68 pio->PIO_IFSCDR = mask;
69 #elif defined PIO_SCIFSR_P0
70 pio->PIO_SCIFSR = mask;
71 #endif
72 }
73 pio->PIO_IFER = mask;
74 } else {
75 pio->PIO_IFDR = mask;
76 }
77
78 /* Configure interrupt */
79 if (flags & SOC_GPIO_INT_ENABLE) {
80 if ((flags & SOC_GPIO_INT_TRIG_MASK) == SOC_GPIO_INT_TRIG_DOUBLE_EDGE) {
81 /* Disable additional interrupt modes, enable the default */
82 pio->PIO_AIMDR = mask;
83 } else {
84 /* Configure additional interrupt mode */
85 if ((flags & SOC_GPIO_INT_TRIG_MASK) == SOC_GPIO_INT_TRIG_EDGE) {
86 /* Select edge detection event */
87 pio->PIO_ESR = mask;
88 } else {
89 /* Select level detection event */
90 pio->PIO_LSR = mask;
91 }
92
93 if (flags & SOC_GPIO_INT_ACTIVE_HIGH) {
94 pio->PIO_REHLSR = mask;
95 } else {
96 pio->PIO_FELLSR = mask;
97 }
98 /* Enable additional interrupt mode */
99 pio->PIO_AIMER = mask;
100 }
101 /* Enable interrupts on the pin(s) */
102 pio->PIO_IER = mask;
103 } else {
104 /* Nothing to do. All interrupts were disabled in the
105 * beginning.
106 */
107 }
108 }
109
configure_output_attr(Pio * pio,uint32_t mask,uint32_t flags)110 static void configure_output_attr(Pio *pio, uint32_t mask, uint32_t flags)
111 {
112 /* Enable control of the I/O line by the PIO_ODSR register */
113 pio->PIO_OWER = mask;
114 }
115
soc_gpio_configure(const struct soc_gpio_pin * pin)116 void soc_gpio_configure(const struct soc_gpio_pin *pin)
117 {
118 uint32_t mask = pin->mask;
119 Pio *pio = pin->regs;
120 uint8_t periph_id = pin->periph_id;
121 uint32_t flags = pin->flags;
122 uint32_t type = pin->flags & SOC_GPIO_FUNC_MASK;
123
124 /* Configure pin attributes common to all functions */
125 configure_common_attr(pio, mask, flags);
126
127 switch (type) {
128 case SOC_GPIO_FUNC_A:
129 #if defined PIO_ABCDSR_P0
130 pio->PIO_ABCDSR[0] &= ~mask;
131 pio->PIO_ABCDSR[1] &= ~mask;
132 #elif defined PIO_ABSR_P0
133 pio->PIO_ABSR &= ~mask;
134 #endif
135 /* Connect pin to the peripheral (disconnect PIO block) */
136 pio->PIO_PDR = mask;
137 break;
138
139 case SOC_GPIO_FUNC_B:
140 #if defined PIO_ABCDSR_P0
141 pio->PIO_ABCDSR[0] |= mask;
142 pio->PIO_ABCDSR[1] &= ~mask;
143 #elif defined PIO_ABSR_P0
144 pio->PIO_ABSR |= mask;
145 #endif
146 /* Connect pin to the peripheral (disconnect PIO block) */
147 pio->PIO_PDR = mask;
148 break;
149
150 #if defined PIO_ABCDSR_P0
151 case SOC_GPIO_FUNC_C:
152 pio->PIO_ABCDSR[0] &= ~mask;
153 pio->PIO_ABCDSR[1] |= mask;
154 /* Connect pin to the peripheral (disconnect PIO block) */
155 pio->PIO_PDR = mask;
156 break;
157
158 case SOC_GPIO_FUNC_D:
159 pio->PIO_ABCDSR[0] |= mask;
160 pio->PIO_ABCDSR[1] |= mask;
161 /* Connect pin to the peripheral (disconnect PIO block) */
162 pio->PIO_PDR = mask;
163 break;
164 #endif
165
166 case SOC_GPIO_FUNC_IN:
167 /* Enable module's clock */
168 soc_pmc_peripheral_enable(periph_id);
169 /* Configure pin attributes related to input function */
170 configure_input_attr(pio, mask, flags);
171 /* Configure pin as input */
172 pio->PIO_ODR = mask;
173 pio->PIO_PER = mask;
174 break;
175
176 case SOC_GPIO_FUNC_OUT_1:
177 case SOC_GPIO_FUNC_OUT_0:
178 /* Set initial pin value */
179 if (type == SOC_GPIO_FUNC_OUT_1) {
180 pio->PIO_SODR = mask;
181 } else {
182 pio->PIO_CODR = mask;
183 }
184
185 /* Configure pin attributes related to output function */
186 configure_output_attr(pio, mask, flags);
187 /* Configure pin(s) as output(s) */
188 pio->PIO_OER = mask;
189 pio->PIO_PER = mask;
190 break;
191
192 default:
193 __ASSERT(0, "Unsupported pin function, check pin.flags value");
194 return;
195 }
196 }
197
soc_gpio_list_configure(const struct soc_gpio_pin pins[],unsigned int size)198 void soc_gpio_list_configure(const struct soc_gpio_pin pins[],
199 unsigned int size)
200 {
201 for (int i = 0; i < size; i++) {
202 soc_gpio_configure(&pins[i]);
203 }
204 }
205