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