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 
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
30 extern "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 
114 static 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 
125 static 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 
136 static 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 
142 static 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 
166 static inline bool _can_bitwise_access(gpio_port_t port, uword_t mask)
167 {
168  if (IS_CT_CONSTANT(port)
169 #ifdef ATMEGA_GPIO_BASE_H
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 
181 static 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 
194 static 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 
207 static 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 
215 static 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 
221 static inline gpio_port_t gpio_get_port(gpio_t pin)
222 {
223  return gpio_port(pin >> 4);
224 }
225 
226 static 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 
239 static 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 
249 static 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 
257 static 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 
265 static inline gpio_port_t gpio_port_pack_addr(void *addr)
266 {
267  return (gpio_port_t)addr;
268 }
269 
270 static 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 
279 static 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:51
#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_port_unpack_addr(gpio_port_t port)
Extract a data pointer that was packed by gpio_port_pack_addr.
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 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.
Definition: architecture.h:69
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.