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#define GPIO_PORT_NUMBERING_ALPHABETIC 1
61
62#ifdef PINA
63/* We sadly cannot use PINA, as PINA is technically (in terms of C spec lingo)
64 * not constant and would trigger:
65 * initializer element is not constant
66 * Hence, the defines are a bit more involved to yield proper constants
67 * suitable for initializers.
68 */
69# define GPIO_PORT_0 (ATMEGA_GPIO_BASE_A)
70#endif
71
72#ifdef PINB
73# define GPIO_PORT_1 (ATMEGA_GPIO_BASE_A + 1 * ATMEGA_GPIO_SIZE)
74#endif
75
76#ifdef PINC
77# define GPIO_PORT_2 (ATMEGA_GPIO_BASE_A + 2 * ATMEGA_GPIO_SIZE)
78#endif
79
80#ifdef PIND
81# define GPIO_PORT_3 (ATMEGA_GPIO_BASE_A + 3 * ATMEGA_GPIO_SIZE)
82#endif
83
84#ifdef PINE
85# define GPIO_PORT_4 (ATMEGA_GPIO_BASE_A + 4 * ATMEGA_GPIO_SIZE)
86#endif
87
88#ifdef PINF
89# define GPIO_PORT_5 (ATMEGA_GPIO_BASE_A + 5 * ATMEGA_GPIO_SIZE)
90#endif
91
92#ifdef PING
93# define GPIO_PORT_6 (ATMEGA_GPIO_BASE_A + 6 * ATMEGA_GPIO_SIZE)
94#endif
95
96/* There is a larger gap between PING and PINH to allow other peripherals to
97 * also be mapped into the fast I/O memory area. */
98#ifdef PINH
99# define GPIO_PORT_7 (ATMEGA_GPIO_BASE_H)
100#endif
101
102#ifdef PINI
103# define GPIO_PORT_8 (ATMEGA_GPIO_BASE_H + 1 * ATMEGA_GPIO_SIZE)
104#endif
105
106#ifdef PINJ
107# define GPIO_PORT_9 (ATMEGA_GPIO_BASE_H + 2 * ATMEGA_GPIO_SIZE)
108#endif
109
110#ifdef PINK
111# define GPIO_PORT_10 (ATMEGA_GPIO_BASE_H + 3 * ATMEGA_GPIO_SIZE)
112#endif
113
114#ifdef PINL
115# define GPIO_PORT_11 (ATMEGA_GPIO_BASE_H + 4 * ATMEGA_GPIO_SIZE)
116#endif
117
118static inline gpio_port_t gpio_port(uword_t num)
119{
120#ifdef PINH
121 if (num >= PORT_H) {
122 return ATMEGA_GPIO_BASE_H + ((num - PORT_H) * ATMEGA_GPIO_SIZE);
123 }
124#endif
125
126 return ATMEGA_GPIO_BASE_A + (num * ATMEGA_GPIO_SIZE);
127}
128
129static inline uword_t gpio_port_num(gpio_port_t port)
130{
131#ifdef PINH
132 if ((port) >= ATMEGA_GPIO_BASE_H) {
133 return (port - ATMEGA_GPIO_BASE_H) / ATMEGA_GPIO_SIZE + PORT_H;
134 }
135#endif
136
137 return (port - ATMEGA_GPIO_BASE_A) / ATMEGA_GPIO_SIZE;
138}
139
140static inline uword_t gpio_ll_read(gpio_port_t port)
141{
142 atmega_gpio_port_t *p = (void *)port;
143 return p->pin;
144}
145
146static inline uword_t gpio_ll_read_output(gpio_port_t port)
147{
148 atmega_gpio_port_t *p = (void *)port;
149 return p->port;
150}
151
170static inline bool _can_bitwise_access(gpio_port_t port, uword_t mask)
171{
172 if (IS_CT_CONSTANT(port)
175 && (port < ATMEGA_GPIO_BASE_H)
176#endif
177 && IS_CT_CONSTANT(mask)
178 && IS_CT_CONSTANT(__builtin_popcount(mask) == 1)) {
179 return __builtin_popcount(mask) == 1;
180 }
181
182 return 0;
183}
184
185static inline void gpio_ll_set(gpio_port_t port, uword_t mask)
186{
187 atmega_gpio_port_t *p = (void *)port;
188 if (_can_bitwise_access(port, mask)) {
189 p->port |= mask;
190 }
191 else {
192 unsigned state = irq_disable();
193 p->port |= mask;
194 irq_restore(state);
195 }
196}
197
198static inline void gpio_ll_clear(gpio_port_t port, uword_t mask)
199{
200 atmega_gpio_port_t *p = (void *)port;
201 if (_can_bitwise_access(port, mask)) {
202 p->port &= ~mask;
203 }
204 else {
205 unsigned state = irq_disable();
206 p->port &= ~mask;
207 irq_restore(state);
208 }
209}
210
211static inline void gpio_ll_toggle(gpio_port_t port, uword_t mask)
212{
213 atmega_gpio_port_t *p = (void *)port;
214 /* this is equivalent to `p->port ^= mask`, but faster and inherently
215 * atomically */
216 p->pin = mask;
217}
218
219static inline void gpio_ll_write(gpio_port_t port, uword_t value)
220{
221 atmega_gpio_port_t *p = (void *)port;
222 p->port = value;
223}
224
225static inline gpio_port_t gpio_get_port(gpio_t pin)
226{
227 return gpio_port(pin >> 4);
228}
229
230static inline uint8_t gpio_get_pin_num(gpio_t pin)
231{
232 return pin & 0x0f;
233}
234
236 uword_t value)
237{
238 atmega_gpio_port_t *p = (void *)port;
239 uword_t result = (gpio_ll_read_output(port) & (~p->ddr)) | value;
240 return result;
241}
242
243static inline uword_t gpio_ll_prepare_write(gpio_port_t port, uword_t mask,
244 uword_t value)
245{
246 atmega_gpio_port_t *p = (void *)port;
247 uword_t result = gpio_ll_read_output(port);
248 result &= (~p->ddr) | (~mask);
249 result |= value;
250 return result;
251}
252
253static inline void gpio_ll_switch_dir_output(gpio_port_t port, uword_t outputs)
254{
255 unsigned irq_state = irq_disable();
256 atmega_gpio_port_t *p = (void *)port;
257 p->ddr |= outputs;
258 irq_restore(irq_state);
259}
260
261static inline void gpio_ll_switch_dir_input(gpio_port_t port, uword_t inputs)
262{
263 unsigned irq_state = irq_disable();
264 atmega_gpio_port_t *p = (void *)port;
265 p->ddr &= ~(inputs);
266 irq_restore(irq_state);
267}
268
269static inline gpio_port_t gpio_port_pack_addr(void *addr)
270{
271 return (gpio_port_t)addr;
272}
273
274static inline void * gpio_port_unpack_addr(gpio_port_t port)
275{
276 if (port < RAMSTART) {
277 return NULL;
278 }
279
280 return (void *)port;
281}
282
283static inline bool is_gpio_port_num_valid(uint_fast8_t num)
284{
285 switch (num) {
286 default:
287 return false;
288#ifdef DDRA
289 case 0:
290#endif
291#ifdef DDRB
292 case 1:
293#endif
294#ifdef DDRC
295 case 2:
296#endif
297#ifdef DDRD
298 case 3:
299#endif
300#ifdef DDRE
301 case 4:
302#endif
303#ifdef DDRF
304 case 5:
305#endif
306#ifdef DDRG
307 case 6:
308#endif
309#ifdef DDRH
310 case 7:
311#endif
312#ifdef DDRI
313 case 8:
314#endif
315#ifdef DDRJ
316 case 9:
317#endif
318#ifdef DDRK
319 case 10:
320#endif
321#ifdef DDRL
322 case 11:
323#endif
324#ifdef DDRM
325 case 12:
326#endif
327#ifdef DDRN
328 case 13:
329#endif
330#ifdef DDRO
331 case 14:
332#endif
333#ifdef DDRP
334 case 15:
335#endif
336#ifdef DDRQ
337 case 16:
338#endif
339#ifdef DDRR
340 case 17:
341#endif
342#ifdef DDRS
343 case 18:
344#endif
345#ifdef DDRT
346 case 19:
347#endif
348#ifdef DDRU
349 case 20:
350#endif
351#ifdef DDRV
352 case 21:
353#endif
354#ifdef DDRW
355 case 22:
356#endif
357#ifdef DDRX
358 case 23:
359#endif
360#ifdef DDRY
361 case 24:
362#endif
363#ifdef DDRZ
364 case 25:
365#endif
366 return true;
367 }
368}
369
370#endif /* DOXYGEN */
371#ifdef __cplusplus
372}
373#endif
374
375#endif /* GPIO_LL_ARCH_H */
POSIX.1-2008 compliant version of the assert macro.
@ PORT_H
port H
Definition periph_cpu.h:52
#define ATMEGA_GPIO_BASE_H
Base of the GPIO registers of the second memory region (port >= H)
#define ATMEGA_GPIO_BASE_A
Base of the GPIO registers as memory address.
#define ATMEGA_GPIO_SIZE
sizeof(atmega_gpio_port_t), but preprocessor friendly
#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_set(gpio_port_t port, uword_t mask)
Perform an reg |= mask operation on the I/O register of the port.
gpio_port_t gpio_port(uword_t num)
Get the gpio_port_t value of the port number num.
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:727
static gpio_port_t gpio_port_pack_addr(void *addr)
Pack a pointer into a gpio_port_t.
static void gpio_ll_switch_dir_output(gpio_port_t port, uword_t pins)
Turn GPIO pins specified by pins (obtained from gpio_ll_prepare_switch_dir) to outputs.
static void gpio_ll_switch_dir_input(gpio_port_t port, uword_t pins)
Turn GPIO pins specified by pins (obtained from gpio_ll_prepare_switch_dir) to inputs.
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:705
uword_t gpio_port_num(gpio_port_t port)
Get the number of the GPIO port port refers to.
static void * gpio_port_unpack_addr(gpio_port_t port)
Extract a data pointer that was packed by gpio_port_pack_addr.
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.
#define RAMSTART
Lowest address of the RAM, peripherals are below.
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.