Loading...
Searching...
No Matches
gpio_ll_arch.h
Go to the documentation of this file.
1/*
2 * Copyright (C) 2015 Freie Universität Berlin
3 *
4 * This file is subject to the terms and conditions of the GNU Lesser
5 * General Public License v2.1. See the file LICENSE in the top level
6 * directory for more details.
7 */
8
23#ifndef GPIO_LL_ARCH_H
24#define GPIO_LL_ARCH_H
25
26#include <assert.h>
27
28#include "cpu.h"
29#include "irq.h"
30#include "kernel_defines.h"
31#include "periph_cpu.h"
32
33#ifdef __cplusplus
34extern "C" {
35#endif
36
37#ifndef DOXYGEN /* hide implementation specific details from Doxygen */
38
39/* AVR generally requires two CPU cycles for loads from and stores to memory.
40 * However, there are special single cycle instructions, which however require
41 * the target address to be given as an 5 bit immediate. So only a 64 byte sized
42 * part of the data address space can be accessed this way, which is called
43 * the I/O memory (as (almost) only memory mapped I/O is mapped there).
44 *
45 * GPIO ports up to G are part of the I/O mapped area, but starting from port H
46 * the GPIO registers are only accessible via regular load and store operations
47 * and mapped slightly different in the data address space. For some reason,
48 * there is a gap between the GPIO memory regions for port A to G and H and
49 * above that results in special handling for GPIO ports H and above.
50 *
51 * Note that this implementation always uses the data address way to access GPIO
52 * registers and never the single cycle instructions. However, GCC converts the
53 * instructions into semantically equivalent single CPU cycle instructions
54 * whenever the target address is known at compile time (so can be expressed as
55 * immediate) and also mapped into the I/O address space. We rely on this
56 * optimization to claim single cycle GPIO accesses for GPIO ports below H,
57 * whenever the port number is known at compile time.
58 */
59
60#ifdef PORTH
61#define GPIO_PORT(num) \
62 ((num >= PORT_H) ? \
63 (ATMEGA_GPIO_BASE_H + ((num) - PORT_H) * ATMEGA_GPIO_SIZE) : \
64 (ATMEGA_GPIO_BASE_A + (num) * ATMEGA_GPIO_SIZE))
65#define GPIO_PORT_NUM(port) \
66 (((port) >= ATMEGA_GPIO_BASE_H) ? \
67 (((port) - ATMEGA_GPIO_BASE_H) / ATMEGA_GPIO_SIZE + PORT_H) : \
68 (((port) - ATMEGA_GPIO_BASE_A) / ATMEGA_GPIO_SIZE))
69#else
70#define GPIO_PORT(num) (ATMEGA_GPIO_BASE_A + (num) * ATMEGA_GPIO_SIZE)
71#define GPIO_PORT_NUM(port) (((port) - ATMEGA_GPIO_BASE_A) / ATMEGA_GPIO_SIZE)
72#endif
73
74static inline uword_t gpio_ll_read(gpio_port_t port)
75{
76 atmega_gpio_port_t *p = (void *)port;
77 return p->pin;
78}
79
80static inline uword_t gpio_ll_read_output(gpio_port_t port)
81{
82 atmega_gpio_port_t *p = (void *)port;
83 return p->port;
84}
85
104static inline bool _can_bitwise_access(gpio_port_t port, uword_t mask)
105{
106 if (IS_CT_CONSTANT(port)
109 && (port < ATMEGA_GPIO_BASE_H)
110#endif
111 && IS_CT_CONSTANT(mask)
112 && IS_CT_CONSTANT(__builtin_popcount(mask) == 1)) {
113 return __builtin_popcount(mask) == 1;
114 }
115
116 return 0;
117}
118
119static inline void gpio_ll_set(gpio_port_t port, uword_t mask)
120{
121 atmega_gpio_port_t *p = (void *)port;
122 if (_can_bitwise_access(port, mask)) {
123 p->port |= mask;
124 }
125 else {
126 unsigned state = irq_disable();
127 p->port |= mask;
128 irq_restore(state);
129 }
130}
131
132static inline void gpio_ll_clear(gpio_port_t port, uword_t mask)
133{
134 atmega_gpio_port_t *p = (void *)port;
135 if (_can_bitwise_access(port, mask)) {
136 p->port &= ~mask;
137 }
138 else {
139 unsigned state = irq_disable();
140 p->port &= ~mask;
141 irq_restore(state);
142 }
143}
144
145static inline void gpio_ll_toggle(gpio_port_t port, uword_t mask)
146{
147 atmega_gpio_port_t *p = (void *)port;
148 /* this is equivalent to `p->port ^= mask`, but faster and inherently
149 * atomically */
150 p->pin = mask;
151}
152
153static inline void gpio_ll_write(gpio_port_t port, uword_t value)
154{
155 atmega_gpio_port_t *p = (void *)port;
156 p->port = value;
157}
158
159static inline gpio_port_t gpio_get_port(gpio_t pin)
160{
161 return GPIO_PORT(pin >> 4);
162}
163
164static inline uint8_t gpio_get_pin_num(gpio_t pin)
165{
166 return pin & 0x0f;
167}
168
170 uword_t value)
171{
172 atmega_gpio_port_t *p = (void *)port;
173 uword_t result = (gpio_ll_read_output(port) & (~p->ddr)) | value;
174 return result;
175}
176
177static inline uword_t gpio_ll_prepare_write(gpio_port_t port, uword_t mask,
178 uword_t value)
179{
180 atmega_gpio_port_t *p = (void *)port;
181 uword_t result = gpio_ll_read_output(port);
182 result &= (~p->ddr) | (~mask);
183 result |= value;
184 return result;
185}
186
187static inline void gpio_ll_switch_dir_output(gpio_port_t port, uword_t outputs)
188{
189 unsigned irq_state = irq_disable();
190 atmega_gpio_port_t *p = (void *)port;
191 p->ddr |= outputs;
192 irq_restore(irq_state);
193}
194
195static inline void gpio_ll_switch_dir_input(gpio_port_t port, uword_t inputs)
196{
197 unsigned irq_state = irq_disable();
198 atmega_gpio_port_t *p = (void *)port;
199 p->ddr &= ~(inputs);
200 irq_restore(irq_state);
201}
202
203static inline gpio_port_t gpio_port_pack_addr(void *addr)
204{
205 return (gpio_port_t)addr;
206}
207
208static inline void * gpio_port_unpack_addr(gpio_port_t port)
209{
210 if (port < RAMSTART) {
211 return NULL;
212 }
213
214 return (void *)port;
215}
216
217static inline bool is_gpio_port_num_valid(uint_fast8_t num)
218{
219 switch (num) {
220 default:
221 return false;
222#ifdef DDRA
223 case 0:
224#endif
225#ifdef DDRB
226 case 1:
227#endif
228#ifdef DDRC
229 case 2:
230#endif
231#ifdef DDRD
232 case 3:
233#endif
234#ifdef DDRE
235 case 4:
236#endif
237#ifdef DDRF
238 case 5:
239#endif
240#ifdef DDRG
241 case 6:
242#endif
243#ifdef DDRH
244 case 7:
245#endif
246#ifdef DDRI
247 case 8:
248#endif
249#ifdef DDRJ
250 case 9:
251#endif
252#ifdef DDRK
253 case 10:
254#endif
255#ifdef DDRL
256 case 11:
257#endif
258#ifdef DDRM
259 case 12:
260#endif
261#ifdef DDRN
262 case 13:
263#endif
264#ifdef DDRO
265 case 14:
266#endif
267#ifdef DDRP
268 case 15:
269#endif
270#ifdef DDRQ
271 case 16:
272#endif
273#ifdef DDRR
274 case 17:
275#endif
276#ifdef DDRS
277 case 18:
278#endif
279#ifdef DDRT
280 case 19:
281#endif
282#ifdef DDRU
283 case 20:
284#endif
285#ifdef DDRV
286 case 21:
287#endif
288#ifdef DDRW
289 case 22:
290#endif
291#ifdef DDRX
292 case 23:
293#endif
294#ifdef DDRY
295 case 24:
296#endif
297#ifdef DDRZ
298 case 25:
299#endif
300 return true;
301 }
302}
303
304#endif /* DOXYGEN */
305#ifdef __cplusplus
306}
307#endif
308
309#endif /* GPIO_LL_ARCH_H */
POSIX.1-2008 compliant version of the assert macro.
#define ATMEGA_GPIO_BASE_H
Base of the GPIO registers of the second memory region (port >= H)
#define IS_CT_CONSTANT(expr)
Check if given variable / expression is detected as compile time constant.
MAYBE_INLINE void irq_restore(unsigned state)
This function restores the IRQ disable bit in the status register to the value contained within passe...
MAYBE_INLINE unsigned irq_disable(void)
This function sets the IRQ disable bit in the status register.
static uint8_t gpio_get_pin_num(gpio_t pin)
Extract the pin number from a gpio_t
static void gpio_ll_switch_dir_output(gpio_port_t port, uword_t outputs)
Turn GPIO pins specified by the bitmask outputs to outputs.
static void gpio_ll_set(gpio_port_t port, uword_t mask)
Perform an reg |= mask operation on the I/O register of the port.
static void gpio_ll_switch_dir_input(gpio_port_t port, uword_t inputs)
Turn GPIO pins specified by the bitmask inputs to inputs.
static uword_t gpio_ll_prepare_write(gpio_port_t port, uword_t mask, uword_t value)
Helper to use gpio_ll_write side-effect free.
Definition gpio_ll.h:702
static gpio_port_t gpio_port_pack_addr(void *addr)
Pack a pointer into a gpio_port_t.
static uword_t gpio_ll_read(gpio_port_t port)
Get the current input value of all GPIO pins of the given port as bitmask.
static gpio_port_t gpio_get_port(gpio_t pin)
Extract the gpio_port_t from a gpio_t
static uword_t gpio_ll_prepare_write_all_outputs(gpio_port_t port, uword_t value)
Same as gpio_ll_prepare_write(port, UWORD_MAX, value), but faster.
Definition gpio_ll.h:680
static void * gpio_port_unpack_addr(gpio_port_t port)
Extract a data pointer that was packed by gpio_port_pack_addr.
#define GPIO_PORT(num)
Get the gpio_port_t value of the port identified by num.
Definition gpio_ll.h:106
static bool is_gpio_port_num_valid(uint_fast8_t num)
Check if the given number is a valid argument for GPIO_PORT.
static uword_t gpio_ll_read_output(gpio_port_t port)
Get the current output value of all GPIO pins of the given port as bitmask.
static void gpio_ll_clear(gpio_port_t port, uword_t mask)
Perform an reg &= ~mask operation on the I/O register of the port.
static void gpio_ll_toggle(gpio_port_t port, uword_t mask)
Perform an reg ^= mask operation on the I/O register of the port.
static void gpio_ll_write(gpio_port_t port, uword_t state)
Perform a masked write operation on the I/O register of the port.
uintptr_t gpio_port_t
GPIO port type.
Definition gpio_ll.h:87
uint< NUM > _t uword_t
Word sized unsigned integer.
IRQ driver interface.
Common macros and compiler attributes/pragmas configuration.
Structure describing the memory layout of the registers of a GPIO port on ATmega MCUs.
volatile uint8_t port
Read/write the state of GPIO pins using the Port Data Register.
volatile uint8_t pin
Toggle bits in the port register.
volatile uint8_t ddr
Configure pins as output (1) or input (0) using the Data Direction Register.