205 lines
4.9 KiB
C
205 lines
4.9 KiB
C
/*
|
|
* Copyright (c) 2016 Piotr Mienkowski
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/** @file
|
|
* @brief Atmel SAM MCU family General Purpose Input Output (GPIO)
|
|
* module HAL driver.
|
|
*/
|
|
|
|
#include <misc/__assert.h>
|
|
#include "soc_gpio.h"
|
|
|
|
/*
|
|
* There exist minor differences between SAM MCU family members in naming
|
|
* of some of the registers. Check that our expectations are met.
|
|
*/
|
|
#if (!defined(PIO_IFSCER_P0) && !defined(PIO_DIFSR_P0)) \
|
|
|| (!defined(PIO_IFSCDR_P0) && !defined(PIO_SCIFSR_P0)) \
|
|
|| (!defined(PIO_ABCDSR_P0) && !defined(PIO_ABSR_P0))
|
|
#error "Unsupported Atmel SAM MCU series"
|
|
#endif
|
|
|
|
static void configure_common_attr(Pio *pio, u32_t mask, u32_t flags)
|
|
{
|
|
/* Disable interrupts on the pin(s) */
|
|
pio->PIO_IDR = mask;
|
|
|
|
/* Configure pull-up(s) */
|
|
if (flags & SOC_GPIO_PULLUP) {
|
|
pio->PIO_PUER = mask;
|
|
} else {
|
|
pio->PIO_PUDR = mask;
|
|
}
|
|
|
|
/* Configure pull-down only for MCU series that support it */
|
|
#if defined PIO_PPDER_P0
|
|
/* Configure pull-down(s) */
|
|
if (flags & SOC_GPIO_PULLDOWN) {
|
|
pio->PIO_PPDER = mask;
|
|
} else {
|
|
pio->PIO_PPDDR = mask;
|
|
}
|
|
#endif
|
|
|
|
/* Configure open drain (multi-drive) */
|
|
if (flags & SOC_GPIO_OPENDRAIN) {
|
|
pio->PIO_MDER = mask;
|
|
} else {
|
|
pio->PIO_MDDR = mask;
|
|
}
|
|
}
|
|
|
|
static void configure_input_attr(Pio *pio, u32_t mask, u32_t flags)
|
|
{
|
|
/* Configure input filter */
|
|
if ((flags & SOC_GPIO_IN_FILTER_MASK) != 0) {
|
|
if ((flags & SOC_GPIO_IN_FILTER_MASK) == SOC_GPIO_IN_FILTER_DEBOUNCE) {
|
|
/* Enable de-bounce, disable de-glitch */
|
|
#if defined PIO_IFSCER_P0
|
|
pio->PIO_IFSCER = mask;
|
|
#elif defined PIO_DIFSR_P0
|
|
pio->PIO_DIFSR = mask;
|
|
#endif
|
|
} else {
|
|
/* Disable de-bounce, enable de-glitch */
|
|
#if defined PIO_IFSCDR_P0
|
|
pio->PIO_IFSCDR = mask;
|
|
#elif defined PIO_SCIFSR_P0
|
|
pio->PIO_SCIFSR = mask;
|
|
#endif
|
|
}
|
|
pio->PIO_IFER = mask;
|
|
} else {
|
|
pio->PIO_IFDR = mask;
|
|
}
|
|
|
|
/* Configure interrupt */
|
|
if (flags & SOC_GPIO_INT_ENABLE) {
|
|
if ((flags & SOC_GPIO_INT_TRIG_MASK) == SOC_GPIO_INT_TRIG_DOUBLE_EDGE) {
|
|
/* Disable additional interrupt modes, enable the default */
|
|
pio->PIO_AIMDR = mask;
|
|
} else {
|
|
/* Configure additional interrupt mode */
|
|
if ((flags & SOC_GPIO_INT_TRIG_MASK) == SOC_GPIO_INT_TRIG_EDGE) {
|
|
/* Select edge detection event */
|
|
pio->PIO_ESR = mask;
|
|
} else {
|
|
/* Select level detection event */
|
|
pio->PIO_LSR = mask;
|
|
}
|
|
|
|
if (flags & SOC_GPIO_INT_ACTIVE_HIGH) {
|
|
pio->PIO_REHLSR = mask;
|
|
} else {
|
|
pio->PIO_FELLSR = mask;
|
|
}
|
|
/* Enable additional interrupt mode */
|
|
pio->PIO_AIMER = mask;
|
|
}
|
|
/* Enable interrupts on the pin(s) */
|
|
pio->PIO_IER = mask;
|
|
} else {
|
|
/* Nothing to do. All interrupts were disabled in the
|
|
* beginning.
|
|
*/
|
|
}
|
|
}
|
|
|
|
static void configure_output_attr(Pio *pio, u32_t mask, u32_t flags)
|
|
{
|
|
/* Enable control of the I/O line by the PIO_ODSR register */
|
|
pio->PIO_OWER = mask;
|
|
}
|
|
|
|
void soc_gpio_configure(const struct soc_gpio_pin *pin)
|
|
{
|
|
u32_t mask = pin->mask;
|
|
Pio *pio = pin->regs;
|
|
u8_t periph_id = pin->periph_id;
|
|
u32_t flags = pin->flags;
|
|
u32_t type = pin->flags & SOC_GPIO_FUNC_MASK;
|
|
|
|
/* Configure pin attributes common to all functions */
|
|
configure_common_attr(pio, mask, flags);
|
|
|
|
switch (type) {
|
|
case SOC_GPIO_FUNC_A:
|
|
#if defined PIO_ABCDSR_P0
|
|
pio->PIO_ABCDSR[0] &= ~mask;
|
|
pio->PIO_ABCDSR[1] &= ~mask;
|
|
#elif defined PIO_ABSR_P0
|
|
pio->PIO_ABSR &= ~mask;
|
|
#endif
|
|
/* Connect pin to the peripheral (disconnect PIO block) */
|
|
pio->PIO_PDR = mask;
|
|
break;
|
|
|
|
case SOC_GPIO_FUNC_B:
|
|
#if defined PIO_ABCDSR_P0
|
|
pio->PIO_ABCDSR[0] |= mask;
|
|
pio->PIO_ABCDSR[1] &= ~mask;
|
|
#elif defined PIO_ABSR_P0
|
|
pio->PIO_ABSR |= mask;
|
|
#endif
|
|
/* Connect pin to the peripheral (disconnect PIO block) */
|
|
pio->PIO_PDR = mask;
|
|
break;
|
|
|
|
#if defined PIO_ABCDSR_P0
|
|
case SOC_GPIO_FUNC_C:
|
|
pio->PIO_ABCDSR[0] &= ~mask;
|
|
pio->PIO_ABCDSR[1] |= mask;
|
|
/* Connect pin to the peripheral (disconnect PIO block) */
|
|
pio->PIO_PDR = mask;
|
|
break;
|
|
|
|
case SOC_GPIO_FUNC_D:
|
|
pio->PIO_ABCDSR[0] |= mask;
|
|
pio->PIO_ABCDSR[1] |= mask;
|
|
/* Connect pin to the peripheral (disconnect PIO block) */
|
|
pio->PIO_PDR = mask;
|
|
break;
|
|
#endif
|
|
|
|
case SOC_GPIO_FUNC_IN:
|
|
/* Enable module's clock */
|
|
soc_pmc_peripheral_enable(periph_id);
|
|
/* Configure pin attributes related to input function */
|
|
configure_input_attr(pio, mask, flags);
|
|
/* Configure pin as input */
|
|
pio->PIO_ODR = mask;
|
|
pio->PIO_PER = mask;
|
|
break;
|
|
|
|
case SOC_GPIO_FUNC_OUT_1:
|
|
case SOC_GPIO_FUNC_OUT_0:
|
|
/* Set initial pin value */
|
|
if (type == SOC_GPIO_FUNC_OUT_1) {
|
|
pio->PIO_SODR = mask;
|
|
} else {
|
|
pio->PIO_CODR = mask;
|
|
}
|
|
|
|
/* Configure pin attributes related to output function */
|
|
configure_output_attr(pio, mask, flags);
|
|
/* Configure pin(s) as output(s) */
|
|
pio->PIO_OER = mask;
|
|
pio->PIO_PER = mask;
|
|
break;
|
|
|
|
default:
|
|
__ASSERT(0, "Unsupported pin function, check pin.flags value");
|
|
return;
|
|
}
|
|
}
|
|
|
|
void soc_gpio_list_configure(const struct soc_gpio_pin pins[],
|
|
unsigned int size)
|
|
{
|
|
for (int i = 0; i < size; i++) {
|
|
soc_gpio_configure(&pins[i]);
|
|
}
|
|
}
|