blob: c7690f7848721841cf64850708adc65aa05c9dc9 [file] [log] [blame]
/*
* 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 <zephyr/sys/__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, uint32_t mask, uint32_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, uint32_t mask, uint32_t flags)
{
/* Configure input filter */
if ((flags & SOC_GPIO_IN_FILTER_MASK) != 0U) {
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, uint32_t mask, uint32_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)
{
uint32_t mask = pin->mask;
Pio *pio = pin->regs;
uint8_t periph_id = pin->periph_id;
uint32_t flags = pin->flags;
uint32_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]);
}
}