Loading...
Searching...
No Matches
gpio_ll_arch.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2015 Freie Universität Berlin
3 * SPDX-License-Identifier: LGPL-2.1-only
4 */
5
6#pragma once
7
21
22#include <assert.h>
23
24#include "cpu.h"
25#include "irq.h"
26#include "kernel_defines.h"
27#include "periph_cpu.h"
28
29#ifdef __cplusplus
30extern "C" {
31#endif
32
33#ifndef DOXYGEN /* hide implementation specific details from Doxygen */
34
35/* AVR generally requires two CPU cycles for loads from and stores to memory.
36 * However, there are special single cycle instructions, which however require
37 * the target address to be given as an 5 bit immediate. So only a 64 byte sized
38 * part of the data address space can be accessed this way, which is called
39 * the I/O memory (as (almost) only memory mapped I/O is mapped there).
40 *
41 * GPIO ports up to G are part of the I/O mapped area, but starting from port H
42 * the GPIO registers are only accessible via regular load and store operations
43 * and mapped slightly different in the data address space. For some reason,
44 * there is a gap between the GPIO memory regions for port A to G and H and
45 * above that results in special handling for GPIO ports H and above.
46 *
47 * Note that this implementation always uses the data address way to access GPIO
48 * registers and never the single cycle instructions. However, GCC converts the
49 * instructions into semantically equivalent single CPU cycle instructions
50 * whenever the target address is known at compile time (so can be expressed as
51 * immediate) and also mapped into the I/O address space. We rely on this
52 * optimization to claim single cycle GPIO accesses for GPIO ports below H,
53 * whenever the port number is known at compile time.
54 */
55
56#define GPIO_PORT_NUMBERING_ALPHABETIC 1
57
58#ifdef PINA
59/* We sadly cannot use PINA, as PINA is technically (in terms of C spec lingo)
60 * not constant and would trigger:
61 * initializer element is not constant
62 * Hence, the defines are a bit more involved to yield proper constants
63 * suitable for initializers.
64 */
65# define GPIO_PORT_0 (ATMEGA_GPIO_BASE_A)
66#endif
67
68#ifdef PINB
69# define GPIO_PORT_1 (ATMEGA_GPIO_BASE_A + 1 * ATMEGA_GPIO_SIZE)
70#endif
71
72#ifdef PINC
73# define GPIO_PORT_2 (ATMEGA_GPIO_BASE_A + 2 * ATMEGA_GPIO_SIZE)
74#endif
75
76#ifdef PIND
77# define GPIO_PORT_3 (ATMEGA_GPIO_BASE_A + 3 * ATMEGA_GPIO_SIZE)
78#endif
79
80#ifdef PINE
81# define GPIO_PORT_4 (ATMEGA_GPIO_BASE_A + 4 * ATMEGA_GPIO_SIZE)
82#endif
83
84#ifdef PINF
85# define GPIO_PORT_5 (ATMEGA_GPIO_BASE_A + 5 * ATMEGA_GPIO_SIZE)
86#endif
87
88#ifdef PING
89# define GPIO_PORT_6 (ATMEGA_GPIO_BASE_A + 6 * ATMEGA_GPIO_SIZE)
90#endif
91
92/* There is a larger gap between PING and PINH to allow other peripherals to
93 * also be mapped into the fast I/O memory area. */
94#ifdef PINH
95# define GPIO_PORT_7 (ATMEGA_GPIO_BASE_H)
96#endif
97
98#ifdef PINI
99# define GPIO_PORT_8 (ATMEGA_GPIO_BASE_H + 1 * ATMEGA_GPIO_SIZE)
100#endif
101
102#ifdef PINJ
103# define GPIO_PORT_9 (ATMEGA_GPIO_BASE_H + 2 * ATMEGA_GPIO_SIZE)
104#endif
105
106#ifdef PINK
107# define GPIO_PORT_10 (ATMEGA_GPIO_BASE_H + 3 * ATMEGA_GPIO_SIZE)
108#endif
109
110#ifdef PINL
111# define GPIO_PORT_11 (ATMEGA_GPIO_BASE_H + 4 * ATMEGA_GPIO_SIZE)
112#endif
113
114static inline gpio_port_t gpio_port(uword_t num)
115{
116#ifdef PINH
117 if (num >= PORT_H) {
118 return ATMEGA_GPIO_BASE_H + ((num - PORT_H) * ATMEGA_GPIO_SIZE);
119 }
120#endif
121
122 return ATMEGA_GPIO_BASE_A + (num * ATMEGA_GPIO_SIZE);
123}
124
125static inline uword_t gpio_port_num(gpio_port_t port)
126{
127#ifdef PINH
128 if ((port) >= ATMEGA_GPIO_BASE_H) {
129 return (port - ATMEGA_GPIO_BASE_H) / ATMEGA_GPIO_SIZE + PORT_H;
130 }
131#endif
132
133 return (port - ATMEGA_GPIO_BASE_A) / ATMEGA_GPIO_SIZE;
134}
135
136static inline uword_t gpio_ll_read(gpio_port_t port)
137{
138 atmega_gpio_port_t *p = (void *)port;
139 return p->pin;
140}
141
142static inline uword_t gpio_ll_read_output(gpio_port_t port)
143{
144 atmega_gpio_port_t *p = (void *)port;
145 return p->port;
146}
147
166static inline bool _can_bitwise_access(gpio_port_t port, uword_t mask)
167{
168 if (IS_CT_CONSTANT(port)
171 && (port < ATMEGA_GPIO_BASE_H)
172#endif
173 && IS_CT_CONSTANT(mask)
174 && IS_CT_CONSTANT(__builtin_popcount(mask) == 1)) {
175 return __builtin_popcount(mask) == 1;
176 }
177
178 return 0;
179}
180
181static inline void gpio_ll_set(gpio_port_t port, uword_t mask)
182{
183 atmega_gpio_port_t *p = (void *)port;
184 if (_can_bitwise_access(port, mask)) {
185 p->port |= mask;
186 }
187 else {
188 unsigned state = irq_disable();
189 p->port |= mask;
190 irq_restore(state);
191 }
192}
193
194static inline void gpio_ll_clear(gpio_port_t port, uword_t mask)
195{
196 atmega_gpio_port_t *p = (void *)port;
197 if (_can_bitwise_access(port, mask)) {
198 p->port &= ~mask;
199 }
200 else {
201 unsigned state = irq_disable();
202 p->port &= ~mask;
203 irq_restore(state);
204 }
205}
206
207static inline void gpio_ll_toggle(gpio_port_t port, uword_t mask)
208{
209 atmega_gpio_port_t *p = (void *)port;
210 /* this is equivalent to `p->port ^= mask`, but faster and inherently
211 * atomically */
212 p->pin = mask;
213}
214
215static inline void gpio_ll_write(gpio_port_t port, uword_t value)
216{
217 atmega_gpio_port_t *p = (void *)port;
218 p->port = value;
219}
220
221static inline gpio_port_t gpio_get_port(gpio_t pin)
222{
223 return gpio_port(pin >> 4);
224}
225
226static inline uint8_t gpio_get_pin_num(gpio_t pin)
227{
228 return pin & 0x0f;
229}
230
232 uword_t value)
233{
234 atmega_gpio_port_t *p = (void *)port;
235 uword_t result = (gpio_ll_read_output(port) & (~p->ddr)) | value;
236 return result;
237}
238
239static inline uword_t gpio_ll_prepare_write(gpio_port_t port, uword_t mask,
240 uword_t value)
241{
242 atmega_gpio_port_t *p = (void *)port;
243 uword_t result = gpio_ll_read_output(port);
244 result &= (~p->ddr) | (~mask);
245 result |= value;
246 return result;
247}
248
249static inline void gpio_ll_switch_dir_output(gpio_port_t port, uword_t outputs)
250{
251 unsigned irq_state = irq_disable();
252 atmega_gpio_port_t *p = (void *)port;
253 p->ddr |= outputs;
254 irq_restore(irq_state);
255}
256
257static inline void gpio_ll_switch_dir_input(gpio_port_t port, uword_t inputs)
258{
259 unsigned irq_state = irq_disable();
260 atmega_gpio_port_t *p = (void *)port;
261 p->ddr &= ~(inputs);
262 irq_restore(irq_state);
263}
264
265static inline gpio_port_t gpio_port_pack_addr(void *addr)
266{
267 return (gpio_port_t)addr;
268}
269
270static inline void * gpio_port_unpack_addr(gpio_port_t port)
271{
272 if (port < RAMSTART) {
273 return NULL;
274 }
275
276 return (void *)port;
277}
278
279static inline bool is_gpio_port_num_valid(uint_fast8_t num)
280{
281 switch (num) {
282 default:
283 return false;
284#ifdef DDRA
285 case 0:
286#endif
287#ifdef DDRB
288 case 1:
289#endif
290#ifdef DDRC
291 case 2:
292#endif
293#ifdef DDRD
294 case 3:
295#endif
296#ifdef DDRE
297 case 4:
298#endif
299#ifdef DDRF
300 case 5:
301#endif
302#ifdef DDRG
303 case 6:
304#endif
305#ifdef DDRH
306 case 7:
307#endif
308#ifdef DDRI
309 case 8:
310#endif
311#ifdef DDRJ
312 case 9:
313#endif
314#ifdef DDRK
315 case 10:
316#endif
317#ifdef DDRL
318 case 11:
319#endif
320#ifdef DDRM
321 case 12:
322#endif
323#ifdef DDRN
324 case 13:
325#endif
326#ifdef DDRO
327 case 14:
328#endif
329#ifdef DDRP
330 case 15:
331#endif
332#ifdef DDRQ
333 case 16:
334#endif
335#ifdef DDRR
336 case 17:
337#endif
338#ifdef DDRS
339 case 18:
340#endif
341#ifdef DDRT
342 case 19:
343#endif
344#ifdef DDRU
345 case 20:
346#endif
347#ifdef DDRV
348 case 21:
349#endif
350#ifdef DDRW
351 case 22:
352#endif
353#ifdef DDRX
354 case 23:
355#endif
356#ifdef DDRY
357 case 24:
358#endif
359#ifdef DDRZ
360 case 25:
361#endif
362 return true;
363 }
364}
365
366#endif /* DOXYGEN */
367#ifdef __cplusplus
368}
369#endif
370
POSIX.1-2008 compliant version of the assert macro.
@ PORT_H
port H
Definition periph_cpu.h:48
#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:734
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:712
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:95
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.