sam0_common: Use Single-cycle I/O Port for GPIO when available

The Cortex-m0 based ATSAM devices can use the Single-cycle I/O Port for
GPIO. This commit modifies the gpio_t type to use this port when
available. It is mapped back to the peripheral memory space for
configuration access.  When it is not available, the _port_iobus() and
_port() functions behave identical, which is the case for the samd51.
This commit is contained in:
Koen Zandberg 2020-08-20 12:30:09 +02:00
parent 613d0cfffd
commit ddb22a3c56
No known key found for this signature in database
GPG Key ID: 0895A893E6D2985B
2 changed files with 53 additions and 7 deletions

View File

@ -75,6 +75,8 @@ typedef uint32_t gpio_t;
*/ */
#ifdef CPU_FAM_SAML11 #ifdef CPU_FAM_SAML11
#define GPIO_PIN(x, y) (((gpio_t)(&PORT_SEC->Group[x])) | y) #define GPIO_PIN(x, y) (((gpio_t)(&PORT_SEC->Group[x])) | y)
#elif defined(PORT_IOBUS) /* Use IOBUS access when available */
#define GPIO_PIN(x, y) (((gpio_t)(&PORT_IOBUS->Group[x])) | y)
#else #else
#define GPIO_PIN(x, y) (((gpio_t)(&PORT->Group[x])) | y) #define GPIO_PIN(x, y) (((gpio_t)(&PORT->Group[x])) | y)
#endif #endif

View File

@ -21,6 +21,19 @@
* @author Kaspar Schleiser <kaspar@schleiser.de> * @author Kaspar Schleiser <kaspar@schleiser.de>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de> * @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* *
* The GPIO implementation here support the PERIPH_GPIO_FAST_READ pseudomodule
* on the cortex-m0/m23 based devices. This trades an increase in power
* consumption for a decrease in GPIO pin read latency. Enable this in your
* makefile with:
*
* ```
* FEATURES_OPTIONAL += periph_gpio_fast_read
* ```
*
* This switches the GPIO reads to use the Cortex-M0+ single-cycle I/O port
* instead of the regular APB acces. The single-cycle I/O port is always used
* for writes when it is available on the device.
*
* @} * @}
*/ */
@ -75,11 +88,29 @@ typedef enum {
static gpio_isr_ctx_t gpio_config[NUMOF_IRQS]; static gpio_isr_ctx_t gpio_config[NUMOF_IRQS];
#endif /* MODULE_PERIPH_GPIO_IRQ */ #endif /* MODULE_PERIPH_GPIO_IRQ */
static inline PortGroup *_port(gpio_t pin) /* The Cortex-m0 based ATSAM devices can use the Single-cycle I/O Port for GPIO.
* When used, the gpio_t is mapped to the IOBUS area and must be mapped back to
* the peripheral memory space for configuration access. When it is not
* available, the _port_iobus() and _port() functions behave identical.
*/
static inline PortGroup *_port_iobus(gpio_t pin)
{ {
return (PortGroup *)(pin & ~(0x1f)); return (PortGroup *)(pin & ~(0x1f));
} }
static inline PortGroup *_port(gpio_t pin)
{
#ifdef PORT_IOBUS
/* Shift the PortGroup address back from the IOBUS region to the peripheral
* region
*/
return (PortGroup *)((uintptr_t)_port_iobus(pin) -
(uintptr_t)PORT_IOBUS + (uintptr_t)PORT);
#else
return _port_iobus(pin);
#endif
}
static inline int _pin_pos(gpio_t pin) static inline int _pin_pos(gpio_t pin)
{ {
return (pin & 0x1f); return (pin & 0x1f);
@ -121,9 +152,15 @@ int gpio_init(gpio_t pin, gpio_mode_t mode)
/* set pin direction */ /* set pin direction */
if (mode & 0x2) { if (mode & 0x2) {
if (IS_ACTIVE(MODULE_PERIPH_GPIO_FAST_READ)) {
port->CTRL.reg |= pin_mask;
}
port->DIRCLR.reg = pin_mask; port->DIRCLR.reg = pin_mask;
} }
else { else {
if (IS_ACTIVE(MODULE_PERIPH_GPIO_FAST_READ)) {
port->CTRL.reg &= ~pin_mask;
}
port->DIRSET.reg = pin_mask; port->DIRSET.reg = pin_mask;
} }
@ -143,9 +180,16 @@ int gpio_init(gpio_t pin, gpio_mode_t mode)
int gpio_read(gpio_t pin) int gpio_read(gpio_t pin)
{ {
PortGroup *port = _port(pin); PortGroup *port;
int mask = _pin_mask(pin); int mask = _pin_mask(pin);
if (IS_ACTIVE(MODULE_PERIPH_GPIO_FAST_READ)) {
port = _port_iobus(pin);
}
else {
port = _port(pin);
}
if (port->DIR.reg & mask) { if (port->DIR.reg & mask) {
return (port->OUT.reg & mask) ? 1 : 0; return (port->OUT.reg & mask) ? 1 : 0;
} }
@ -156,25 +200,25 @@ int gpio_read(gpio_t pin)
void gpio_set(gpio_t pin) void gpio_set(gpio_t pin)
{ {
_port(pin)->OUTSET.reg = _pin_mask(pin); _port_iobus(pin)->OUTSET.reg = _pin_mask(pin);
} }
void gpio_clear(gpio_t pin) void gpio_clear(gpio_t pin)
{ {
_port(pin)->OUTCLR.reg = _pin_mask(pin); _port_iobus(pin)->OUTCLR.reg = _pin_mask(pin);
} }
void gpio_toggle(gpio_t pin) void gpio_toggle(gpio_t pin)
{ {
_port(pin)->OUTTGL.reg = _pin_mask(pin); _port_iobus(pin)->OUTTGL.reg = _pin_mask(pin);
} }
void gpio_write(gpio_t pin, int value) void gpio_write(gpio_t pin, int value)
{ {
if (value) { if (value) {
_port(pin)->OUTSET.reg = _pin_mask(pin); _port_iobus(pin)->OUTSET.reg = _pin_mask(pin);
} else { } else {
_port(pin)->OUTCLR.reg = _pin_mask(pin); _port_iobus(pin)->OUTCLR.reg = _pin_mask(pin);
} }
} }