mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
209 lines
5.2 KiB
C
209 lines
5.2 KiB
C
/*
|
|
* Copyright (C) 2024 Marian Buschsieweke
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup cpu_msp430
|
|
* @ingroup drivers_periph_gpio_ll
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief CPU specific part of the Peripheral GPIO Low-Level API
|
|
*
|
|
* @author Marian Buschsieweke <marian.buschsieweke@posteo.net>
|
|
*/
|
|
|
|
#ifndef GPIO_LL_ARCH_H
|
|
#define GPIO_LL_ARCH_H
|
|
|
|
#include "cpu.h"
|
|
#include "periph_cpu.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#ifndef DOXYGEN /* hide implementation specific details from Doxygen */
|
|
|
|
/* the memory layout of all GPIO peripherals is compatible, but the location
|
|
* in the address space is pretty much random */
|
|
|
|
#define GPIO_PORT_1 ((gpio_port_t)&PORT_1.base)
|
|
#define GPIO_PORT_2 ((gpio_port_t)&PORT_2.base)
|
|
#define GPIO_PORT_3 ((gpio_port_t)&PORT_3.base)
|
|
#define GPIO_PORT_4 ((gpio_port_t)&PORT_4.base)
|
|
#define GPIO_PORT_5 ((gpio_port_t)&PORT_5.base)
|
|
#define GPIO_PORT_6 ((gpio_port_t)&PORT_6.base)
|
|
/* Port 7 and 8 have different memory layout and are only available on F2xx/G2xx
|
|
* MCUs */
|
|
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
|
# define GPIO_PORT_7 ((gpio_port_t)&PORT_7)
|
|
# define GPIO_PORT_8 ((gpio_port_t)&PORT_8)
|
|
#endif
|
|
|
|
/* IMPORTANT IMPLEMENTATION INFO
|
|
* =============================
|
|
*
|
|
* - MSP430 F2xx/G2xx do have PORT 7 and PORT 8, but those have an incompatible
|
|
* memory layout compared to the other ports. Hence, they need extra handling.
|
|
* However, constant folding should get ride of the branch and overhead if the
|
|
* GPIO port is a compile time constant
|
|
* - MSP430 has bit manipulation instructions that work on memory. E.g.
|
|
* `BIC.B %[mask], @%[ptr]` will implement `*ptr &= ~(mask)` in a single
|
|
* instruction. Same for setting or XORing bits. Hence, the code below
|
|
* may often look like it is missing `irq_disable()` ... `irq_restore()`, but
|
|
* in fact will be atomic due to the MSP430 instruction set.
|
|
*/
|
|
|
|
gpio_port_t gpio_port(uword_t num);
|
|
|
|
static inline uword_t gpio_ll_read(gpio_port_t port)
|
|
{
|
|
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
|
if (port >= (uintptr_t)(&PORT_7)) {
|
|
const msp430_port_p7_p8_t *p = (void *)port;
|
|
return p->IN;
|
|
}
|
|
#endif
|
|
const msp430_port_t *p = (void *)port;
|
|
return p->IN;
|
|
}
|
|
|
|
static inline uword_t gpio_ll_read_output(gpio_port_t port)
|
|
{
|
|
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
|
if (port >= (uintptr_t)(&PORT_7)) {
|
|
const msp430_port_p7_p8_t *p = (void *)port;
|
|
return p->OD;
|
|
}
|
|
#endif
|
|
const msp430_port_t *p = (void *)port;
|
|
return p->OD;
|
|
}
|
|
|
|
static inline void gpio_ll_set(gpio_port_t port, uword_t mask)
|
|
{
|
|
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
|
if (port >= (uintptr_t)(&PORT_7)) {
|
|
msp430_port_p7_p8_t *p = (void *)port;
|
|
p->OD |= mask;
|
|
return;
|
|
}
|
|
#endif
|
|
msp430_port_t *p = (void *)port;
|
|
p->OD |= mask;
|
|
}
|
|
|
|
static inline void gpio_ll_clear(gpio_port_t port, uword_t mask)
|
|
{
|
|
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
|
if (port >= (uintptr_t)(&PORT_7)) {
|
|
msp430_port_p7_p8_t *p = (void *)port;
|
|
p->OD &= ~(mask);
|
|
return;
|
|
}
|
|
#endif
|
|
msp430_port_t *p = (void *)port;
|
|
p->OD &= ~(mask);
|
|
}
|
|
|
|
static inline void gpio_ll_toggle(gpio_port_t port, uword_t mask)
|
|
{
|
|
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
|
if (port >= (uintptr_t)(&PORT_7)) {
|
|
msp430_port_p7_p8_t *p = (void *)port;
|
|
p->OD ^= mask;
|
|
return;
|
|
}
|
|
#endif
|
|
msp430_port_t *p = (void *)port;
|
|
p->OD ^= mask;
|
|
}
|
|
|
|
static inline void gpio_ll_write(gpio_port_t port, uword_t value)
|
|
{
|
|
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
|
if (port >= (uintptr_t)(&PORT_7)) {
|
|
msp430_port_p7_p8_t *p = (void *)port;
|
|
p->OD = value;
|
|
return;
|
|
}
|
|
#endif
|
|
msp430_port_t *p = (void *)port;
|
|
p->OD = value;
|
|
}
|
|
|
|
static inline gpio_port_t gpio_get_port(gpio_t pin)
|
|
{
|
|
return gpio_port(gpio_get_pin_num(pin));
|
|
}
|
|
|
|
static inline uint8_t gpio_get_pin_num(gpio_t pin)
|
|
{
|
|
return pin >> 8;
|
|
}
|
|
|
|
static inline void gpio_ll_switch_dir_output(gpio_port_t port, uword_t outputs)
|
|
{
|
|
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
|
if (port >= (uintptr_t)(&PORT_7)) {
|
|
msp430_port_p7_p8_t *p = (void *)port;
|
|
p->DIR |= outputs;
|
|
return;
|
|
}
|
|
#endif
|
|
msp430_port_t *p = (void *)port;
|
|
p->DIR |= outputs;
|
|
}
|
|
|
|
static inline void gpio_ll_switch_dir_input(gpio_port_t port, uword_t inputs)
|
|
{
|
|
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
|
if (port >= (uintptr_t)(&PORT_7)) {
|
|
msp430_port_p7_p8_t *p = (void *)port;
|
|
p->DIR &= ~(inputs);
|
|
return;
|
|
}
|
|
#endif
|
|
msp430_port_t *p = (void *)port;
|
|
p->DIR &= ~(inputs);
|
|
}
|
|
|
|
static inline gpio_port_t gpio_port_pack_addr(void *addr)
|
|
{
|
|
return (gpio_port_t)addr;
|
|
}
|
|
|
|
static inline void * gpio_port_unpack_addr(gpio_port_t port)
|
|
{
|
|
if (port < RAMSTART) {
|
|
return NULL;
|
|
}
|
|
|
|
return (void *)port;
|
|
}
|
|
|
|
static inline bool is_gpio_port_num_valid(uint_fast8_t num)
|
|
{
|
|
#if defined(CPU_FAM_MSP430_F2XX_G2XX)
|
|
return (num > 0) && (num <= 8);
|
|
#else
|
|
return (num > 0) && (num <= 6);
|
|
#endif
|
|
}
|
|
|
|
uword_t gpio_port_num(gpio_port_t port);
|
|
|
|
#endif /* DOXYGEN */
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* GPIO_LL_ARCH_H */
|
|
/** @} */
|