mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-09-02 00:31:57 +00:00
It needs to verify if the callback was not already installed, and if so: if is was in controller's list. It should return an error in case the node is not found though it was requested to be removed. If already inserted, it will be silently removed but added again, to avoid circular list as stated in the bug. Fixes #11394 Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
399 lines
9.3 KiB
C
399 lines
9.3 KiB
C
/*
|
|
* Copyright (c) 2018 Justin Watson
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <kernel.h>
|
|
#include <device.h>
|
|
#include <init.h>
|
|
#include <soc.h>
|
|
#include <gpio.h>
|
|
|
|
#include "gpio_utils.h"
|
|
|
|
typedef void (*config_func_t)(struct device *dev);
|
|
|
|
struct gpio_sam_config {
|
|
Pio *regs;
|
|
config_func_t config_func;
|
|
u32_t periph_id;
|
|
};
|
|
|
|
struct gpio_sam_runtime {
|
|
sys_slist_t cb;
|
|
};
|
|
|
|
#define DEV_CFG(dev) \
|
|
((const struct gpio_sam_config *const)(dev)->config->config_info)
|
|
|
|
static int gpio_sam_config_pin(Pio * const pio, u32_t mask, int flags)
|
|
{
|
|
/* Setup the pin direcion. */
|
|
if ((flags & GPIO_DIR_MASK) == GPIO_DIR_OUT) {
|
|
pio->PIO_OER = mask;
|
|
} else {
|
|
pio->PIO_ODR = mask;
|
|
}
|
|
|
|
/* Setup interrupt configuration. */
|
|
if (flags & GPIO_INT) {
|
|
if (flags & GPIO_INT_DOUBLE_EDGE) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/* Enable the interrupt. */
|
|
pio->PIO_IER = mask;
|
|
|
|
/* Enable the additional interrupt modes. */
|
|
pio->PIO_AIMER = mask;
|
|
|
|
if (flags & GPIO_INT_EDGE) {
|
|
pio->PIO_ESR = mask;
|
|
} else {
|
|
pio->PIO_LSR = mask;
|
|
}
|
|
|
|
if (flags & GPIO_INT_ACTIVE_HIGH) {
|
|
/* Set to high-level or rising edge. */
|
|
pio->PIO_REHLSR = mask;
|
|
} else {
|
|
/* Set to low-level or falling edge. */
|
|
pio->PIO_FELLSR = mask;
|
|
}
|
|
} else {
|
|
/* Disable the interrupt. */
|
|
pio->PIO_IDR = mask;
|
|
|
|
/* Disable ther additional interrupt modes. */
|
|
pio->PIO_AIMDR = mask;
|
|
}
|
|
|
|
/* Setup Pull-up resistor. */
|
|
if ((flags & GPIO_PUD_MASK) == GPIO_PUD_PULL_UP) {
|
|
/* Enable pull-up. */
|
|
pio->PIO_PUER = mask;
|
|
} else {
|
|
pio->PIO_PUDR = mask;
|
|
}
|
|
|
|
/* Setup Pull-down resistor. */
|
|
if ((flags & GPIO_PUD_MASK) == GPIO_PUD_PULL_DOWN) {
|
|
pio->PIO_PPDER = mask;
|
|
} else {
|
|
pio->PIO_PPDDR = mask;
|
|
}
|
|
|
|
#if defined(SOC_SERIES_SAM3X)
|
|
/* Setup debounce. */
|
|
if (flags & GPIO_INT_DEBOUNCE) {
|
|
pio->PIO_DIFSR = mask;
|
|
} else {
|
|
pio->PIO_SCIFSR = mask;
|
|
}
|
|
#elif defined(SOC_SERIES_SAM4S) || defined(SOC_SERIES_SAME70)
|
|
/* Setup debounce. */
|
|
if (flags & GPIO_INT_DEBOUNCE) {
|
|
pio->PIO_IFSCER = mask;
|
|
} else {
|
|
pio->PIO_IFSCDR = mask;
|
|
}
|
|
#endif
|
|
|
|
/* Enable the PIO to control the pin (instead of a peripheral). */
|
|
pio->PIO_PER = mask;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gpio_sam_config(struct device *dev, int access_op, u32_t pin,
|
|
int flags)
|
|
{
|
|
const struct gpio_sam_config * const cfg = DEV_CFG(dev);
|
|
Pio * const pio = cfg->regs;
|
|
int i, result;
|
|
|
|
switch (access_op) {
|
|
case GPIO_ACCESS_BY_PIN:
|
|
gpio_sam_config_pin(pio, BIT(pin), flags);
|
|
break;
|
|
case GPIO_ACCESS_BY_PORT:
|
|
for (i = 0; i < 32; i++) {
|
|
result = gpio_sam_config_pin(pio, BIT(i), flags);
|
|
if (result < 0) {
|
|
return result;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gpio_sam_write(struct device *dev, int access_op, u32_t pin,
|
|
u32_t value)
|
|
{
|
|
const struct gpio_sam_config * const cfg = DEV_CFG(dev);
|
|
Pio *const pio = cfg->regs;
|
|
u32_t mask = 1 << pin;
|
|
|
|
switch (access_op) {
|
|
case GPIO_ACCESS_BY_PIN:
|
|
if (value) {
|
|
/* Set the pin. */
|
|
pio->PIO_SODR = mask;
|
|
} else {
|
|
/* Clear the pin. */
|
|
pio->PIO_CODR = mask;
|
|
}
|
|
break;
|
|
case GPIO_ACCESS_BY_PORT:
|
|
pio->PIO_OWER = pio->PIO_OSR; /* Write those out pin only */
|
|
pio->PIO_ODSR = value;
|
|
pio->PIO_OWDR = 0xffffffff; /* Disable write ODSR */
|
|
break;
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gpio_sam_read(struct device *dev, int access_op, u32_t pin,
|
|
u32_t *value)
|
|
{
|
|
const struct gpio_sam_config * const cfg = DEV_CFG(dev);
|
|
Pio *const pio = cfg->regs;
|
|
|
|
*value = pio->PIO_PDSR;
|
|
|
|
switch (access_op) {
|
|
case GPIO_ACCESS_BY_PIN:
|
|
*value = (*value >> pin) & 1;
|
|
break;
|
|
case GPIO_ACCESS_BY_PORT:
|
|
break;
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void gpio_sam_isr(void *arg)
|
|
{
|
|
struct device *dev = (struct device *)arg;
|
|
const struct gpio_sam_config * const cfg = DEV_CFG(dev);
|
|
Pio *const pio = cfg->regs;
|
|
struct gpio_sam_runtime *context = dev->driver_data;
|
|
u32_t int_stat;
|
|
|
|
int_stat = pio->PIO_ISR;
|
|
|
|
_gpio_fire_callbacks(&context->cb, dev, int_stat);
|
|
}
|
|
|
|
static int gpio_sam_manage_callback(struct device *port,
|
|
struct gpio_callback *callback,
|
|
bool set)
|
|
{
|
|
struct gpio_sam_runtime *context = port->driver_data;
|
|
|
|
return _gpio_manage_callback(&context->cb, callback, set);
|
|
}
|
|
|
|
static int gpio_sam_enable_callback(struct device *port,
|
|
int access_op, u32_t pin)
|
|
{
|
|
const struct gpio_sam_config * const cfg = DEV_CFG(port);
|
|
Pio *const pio = cfg->regs;
|
|
u32_t mask;
|
|
|
|
switch (access_op) {
|
|
case GPIO_ACCESS_BY_PIN:
|
|
mask = BIT(pin);
|
|
break;
|
|
case GPIO_ACCESS_BY_PORT:
|
|
mask = 0xFFFFFFFF;
|
|
break;
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
pio->PIO_IER |= mask;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gpio_sam_disable_callback(struct device *port,
|
|
int access_op, u32_t pin)
|
|
{
|
|
const struct gpio_sam_config * const cfg = DEV_CFG(port);
|
|
Pio *const pio = cfg->regs;
|
|
u32_t mask;
|
|
|
|
switch (access_op) {
|
|
case GPIO_ACCESS_BY_PIN:
|
|
mask = BIT(pin);
|
|
break;
|
|
case GPIO_ACCESS_BY_PORT:
|
|
mask = 0xFFFFFFFF;
|
|
break;
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
pio->PIO_IDR |= mask;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct gpio_driver_api gpio_sam_api = {
|
|
.config = gpio_sam_config,
|
|
.write = gpio_sam_write,
|
|
.read = gpio_sam_read,
|
|
.manage_callback = gpio_sam_manage_callback,
|
|
.enable_callback = gpio_sam_enable_callback,
|
|
.disable_callback = gpio_sam_disable_callback,
|
|
};
|
|
|
|
int gpio_sam_init(struct device *dev)
|
|
{
|
|
const struct gpio_sam_config * const cfg = DEV_CFG(dev);
|
|
|
|
/* The peripheral clock must be enabled for the interrupts to work. */
|
|
soc_pmc_peripheral_enable(cfg->periph_id);
|
|
|
|
cfg->config_func(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* PORT A */
|
|
#ifdef DT_GPIO_SAM_PORTA_BASE_ADDRESS
|
|
static void port_a_sam_config_func(struct device *dev);
|
|
|
|
static const struct gpio_sam_config port_a_sam_config = {
|
|
.regs = (Pio *)DT_GPIO_SAM_PORTA_BASE_ADDRESS,
|
|
.periph_id = DT_GPIO_SAM_PORTA_PERIPHERAL_ID,
|
|
.config_func = port_a_sam_config_func,
|
|
};
|
|
|
|
static struct gpio_sam_runtime port_a_sam_runtime;
|
|
|
|
DEVICE_AND_API_INIT(port_a_sam, DT_GPIO_SAM_PORTA_LABEL, gpio_sam_init,
|
|
&port_a_sam_runtime, &port_a_sam_config, POST_KERNEL,
|
|
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &gpio_sam_api);
|
|
|
|
static void port_a_sam_config_func(struct device *dev)
|
|
{
|
|
IRQ_CONNECT(DT_GPIO_SAM_PORTA_IRQ, DT_GPIO_SAM_PORTA_IRQ_PRIO,
|
|
gpio_sam_isr, DEVICE_GET(port_a_sam), 0);
|
|
irq_enable(DT_GPIO_SAM_PORTA_IRQ);
|
|
}
|
|
|
|
#endif /* DT_GPIO_SAM_PORTA_BASE_ADDRESS */
|
|
|
|
/* PORT B */
|
|
#ifdef DT_GPIO_SAM_PORTB_BASE_ADDRESS
|
|
static void port_b_sam_config_func(struct device *dev);
|
|
|
|
static const struct gpio_sam_config port_b_sam_config = {
|
|
.regs = (Pio *)DT_GPIO_SAM_PORTB_BASE_ADDRESS,
|
|
.periph_id = DT_GPIO_SAM_PORTB_PERIPHERAL_ID,
|
|
.config_func = port_b_sam_config_func,
|
|
};
|
|
|
|
static struct gpio_sam_runtime port_b_sam_runtime;
|
|
|
|
DEVICE_AND_API_INIT(port_b_sam, DT_GPIO_SAM_PORTB_LABEL, gpio_sam_init,
|
|
&port_b_sam_runtime, &port_b_sam_config, POST_KERNEL,
|
|
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &gpio_sam_api);
|
|
|
|
static void port_b_sam_config_func(struct device *dev)
|
|
{
|
|
IRQ_CONNECT(DT_GPIO_SAM_PORTB_IRQ, DT_GPIO_SAM_PORTB_IRQ_PRIO,
|
|
gpio_sam_isr, DEVICE_GET(port_b_sam), 0);
|
|
irq_enable(DT_GPIO_SAM_PORTB_IRQ);
|
|
}
|
|
|
|
#endif /* DT_GPIO_SAM_PORTB_BASE_ADDRESS */
|
|
|
|
/* PORT C */
|
|
#ifdef DT_GPIO_SAM_PORTC_BASE_ADDRESS
|
|
static void port_c_sam_config_func(struct device *dev);
|
|
|
|
static const struct gpio_sam_config port_c_sam_config = {
|
|
.regs = (Pio *)DT_GPIO_SAM_PORTC_BASE_ADDRESS,
|
|
.periph_id = DT_GPIO_SAM_PORTC_PERIPHERAL_ID,
|
|
.config_func = port_c_sam_config_func,
|
|
};
|
|
|
|
static struct gpio_sam_runtime port_c_sam_runtime;
|
|
|
|
DEVICE_AND_API_INIT(port_c_sam, DT_GPIO_SAM_PORTC_LABEL, gpio_sam_init,
|
|
&port_c_sam_runtime, &port_c_sam_config, POST_KERNEL,
|
|
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &gpio_sam_api);
|
|
|
|
static void port_c_sam_config_func(struct device *dev)
|
|
{
|
|
IRQ_CONNECT(DT_GPIO_SAM_PORTC_IRQ, DT_GPIO_SAM_PORTC_IRQ_PRIO,
|
|
gpio_sam_isr, DEVICE_GET(port_c_sam), 0);
|
|
irq_enable(DT_GPIO_SAM_PORTC_IRQ);
|
|
}
|
|
|
|
#endif /* DT_GPIO_SAM_PORTC_BASE_ADDRESS */
|
|
|
|
/* PORT D */
|
|
#ifdef DT_GPIO_SAM_PORTD_BASE_ADDRESS
|
|
static void port_d_sam_config_func(struct device *dev);
|
|
|
|
static const struct gpio_sam_config port_d_sam_config = {
|
|
.regs = (Pio *)DT_GPIO_SAM_PORTD_BASE_ADDRESS,
|
|
.periph_id = DT_GPIO_SAM_PORTD_PERIPHERAL_ID,
|
|
.config_func = port_d_sam_config_func,
|
|
};
|
|
|
|
static struct gpio_sam_runtime port_d_sam_runtime;
|
|
|
|
DEVICE_AND_API_INIT(port_d_sam, DT_GPIO_SAM_PORTD_LABEL, gpio_sam_init,
|
|
&port_d_sam_runtime, &port_d_sam_config, POST_KERNEL,
|
|
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &gpio_sam_api);
|
|
|
|
static void port_d_sam_config_func(struct device *dev)
|
|
{
|
|
IRQ_CONNECT(DT_GPIO_SAM_PORTD_IRQ, DT_GPIO_SAM_PORTD_IRQ_PRIO,
|
|
gpio_sam_isr, DEVICE_GET(port_d_sam), 0);
|
|
irq_enable(DT_GPIO_SAM_PORTD_IRQ);
|
|
}
|
|
|
|
#endif /* DT_GPIO_SAM_PORTD_BASE_ADDRESS */
|
|
|
|
/* PORT E */
|
|
#ifdef DT_GPIO_SAM_PORTE_BASE_ADDRESS
|
|
static void port_e_sam_config_func(struct device *dev);
|
|
|
|
static const struct gpio_sam_config port_e_sam_config = {
|
|
.regs = (Pio *)DT_GPIO_SAM_PORTE_BASE_ADDRESS,
|
|
.periph_id = DT_GPIO_SAM_PORTE_PERIPHERAL_ID,
|
|
.config_func = port_e_sam_config_func,
|
|
};
|
|
|
|
static struct gpio_sam_runtime port_e_sam_runtime;
|
|
|
|
DEVICE_AND_API_INIT(port_e_sam, DT_GPIO_SAM_PORTE_LABEL, gpio_sam_init,
|
|
&port_e_sam_runtime, &port_e_sam_config, POST_KERNEL,
|
|
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &gpio_sam_api);
|
|
|
|
static void port_e_sam_config_func(struct device *dev)
|
|
{
|
|
IRQ_CONNECT(DT_GPIO_SAM_PORTE_IRQ, DT_GPIO_SAM_PORTE_IRQ_PRIO,
|
|
gpio_sam_isr, DEVICE_GET(port_e_sam), 0);
|
|
irq_enable(DT_GPIO_SAM_PORTE_IRQ);
|
|
}
|
|
|
|
#endif /* DT_GPIO_SAM_PORTE_BASE_ADDRESS */
|