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
34 extern "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 
118 static 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 
129 static 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 
140 static 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 
146 static 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 
170 static inline bool _can_bitwise_access(gpio_port_t port, uword_t mask)
171 {
172  if (IS_CT_CONSTANT(port)
173 #ifdef ATMEGA_GPIO_BASE_H
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 
185 static 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 
198 static 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 
211 static 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 
219 static 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 
225 static inline gpio_port_t gpio_get_port(gpio_t pin)
226 {
227  return gpio_port(pin >> 4);
228 }
229 
230 static 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 
243 static 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 
253 static 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 
261 static 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 
269 static inline gpio_port_t gpio_port_pack_addr(void *addr)
270 {
271  return (gpio_port_t)addr;
272 }
273 
274 static 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 
283 static 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_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: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 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.
Definition: architecture.h:70
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.