| /* |
| * Copyright (c) 2016 Intel Corporation. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @file Driver for the Atmel SAM3 PIO Controller. |
| */ |
| |
| #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); |
| |
| /* Configuration data */ |
| struct gpio_sam3_config { |
| Pio *port; |
| config_func_t config_func; |
| }; |
| |
| struct gpio_sam3_runtime { |
| /* callbacks */ |
| sys_slist_t cb; |
| }; |
| |
| static void _config(struct device *dev, u32_t mask, int flags) |
| { |
| const struct gpio_sam3_config *cfg = dev->config->config_info; |
| |
| /* Disable the pin and return as setup is meaningless now */ |
| if (flags & GPIO_PIN_DISABLE) { |
| cfg->port->PIO_PDR = mask; |
| return; |
| } |
| |
| /* Setup the pin direction */ |
| if ((flags & GPIO_DIR_MASK) == GPIO_DIR_OUT) { |
| cfg->port->PIO_OER = mask; |
| } else { |
| cfg->port->PIO_ODR = mask; |
| } |
| |
| /* Setup interrupt config */ |
| if (flags & GPIO_INT) { |
| if (flags & GPIO_INT_DOUBLE_EDGE) { |
| cfg->port->PIO_AIMDR = mask; |
| } else { |
| cfg->port->PIO_AIMER = mask; |
| |
| if (flags & GPIO_INT_EDGE) { |
| cfg->port->PIO_ESR = mask; |
| } else { |
| cfg->port->PIO_LSR = mask; |
| } |
| |
| if (flags & GPIO_INT_ACTIVE_HIGH) { |
| /* Trigger in high level or rising edge */ |
| cfg->port->PIO_REHLSR = mask; |
| } else { |
| /* Trigger in low level or falling edge */ |
| cfg->port->PIO_FELLSR = mask; |
| } |
| } |
| } |
| |
| /* Pull-up? */ |
| if ((flags & GPIO_PUD_MASK) == GPIO_PUD_PULL_UP) { |
| /* Enable pull-up */ |
| cfg->port->PIO_PUER = mask; |
| } else { |
| /* Disable pull-up */ |
| cfg->port->PIO_PUDR = mask; |
| } |
| |
| /* Debounce */ |
| if (flags & GPIO_INT_DEBOUNCE) { |
| cfg->port->PIO_DIFSR = mask; |
| } else { |
| cfg->port->PIO_SCIFSR = mask; |
| } |
| |
| /* Enable the pin last after pin setup */ |
| if (flags & GPIO_PIN_ENABLE) { |
| cfg->port->PIO_PER = mask; |
| } |
| } |
| |
| /** |
| * @brief Configure pin or port |
| * |
| * @param dev Device struct |
| * @param access_op Access operation (pin or port) |
| * @param pin The pin number |
| * @param flags Flags of pin or port |
| * |
| * @return 0 if successful, failed otherwise |
| */ |
| static int gpio_sam3_config(struct device *dev, int access_op, |
| u32_t pin, int flags) |
| { |
| switch (access_op) { |
| case GPIO_ACCESS_BY_PIN: |
| _config(dev, BIT(pin), flags); |
| break; |
| case GPIO_ACCESS_BY_PORT: |
| _config(dev, (0xFFFFFFFF), flags); |
| break; |
| default: |
| return -ENOTSUP; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * @brief Set the pin or port output |
| * |
| * @param dev Device struct |
| * @param access_op Access operation (pin or port) |
| * @param pin The pin number |
| * @param value Value to set (0 or 1) |
| * |
| * @return 0 if successful, failed otherwise |
| */ |
| static int gpio_sam3_write(struct device *dev, int access_op, |
| u32_t pin, u32_t value) |
| { |
| const struct gpio_sam3_config *cfg = dev->config->config_info; |
| |
| switch (access_op) { |
| case GPIO_ACCESS_BY_PIN: |
| if (value) { |
| /* set the pin */ |
| cfg->port->PIO_SODR = BIT(pin); |
| } else { |
| /* clear the pin */ |
| cfg->port->PIO_CODR = BIT(pin); |
| } |
| break; |
| case GPIO_ACCESS_BY_PORT: |
| if (value) { |
| /* set all pins */ |
| cfg->port->PIO_SODR = 0xFFFFFFFF; |
| } else { |
| /* clear all pins */ |
| cfg->port->PIO_CODR = 0xFFFFFFFF; |
| } |
| break; |
| default: |
| return -ENOTSUP; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * @brief Read the pin or port status |
| * |
| * @param dev Device struct |
| * @param access_op Access operation (pin or port) |
| * @param pin The pin number |
| * @param value Value of input pin(s) |
| * |
| * @return 0 if successful, failed otherwise |
| */ |
| static int gpio_sam3_read(struct device *dev, int access_op, |
| u32_t pin, u32_t *value) |
| { |
| const struct gpio_sam3_config *cfg = dev->config->config_info; |
| |
| *value = cfg->port->PIO_PDSR; |
| |
| switch (access_op) { |
| case GPIO_ACCESS_BY_PIN: |
| *value = (*value >> pin) & 0x01; |
| break; |
| case GPIO_ACCESS_BY_PORT: |
| break; |
| default: |
| return -ENOTSUP; |
| } |
| |
| return 0; |
| } |
| |
| static void gpio_sam3_isr(void *arg) |
| { |
| struct device *dev = (struct device *)arg; |
| const struct gpio_sam3_config *cfg = dev->config->config_info; |
| struct gpio_sam3_runtime *context = dev->driver_data; |
| u32_t int_stat; |
| |
| int_stat = cfg->port->PIO_ISR; |
| |
| _gpio_fire_callbacks(&context->cb, dev, int_stat); |
| } |
| |
| static int gpio_sam3_manage_callback(struct device *dev, |
| struct gpio_callback *callback, |
| bool set) |
| { |
| struct gpio_sam3_runtime *context = dev->driver_data; |
| |
| _gpio_manage_callback(&context->cb, callback, set); |
| |
| return 0; |
| } |
| |
| static int gpio_sam3_enable_callback(struct device *dev, |
| int access_op, u32_t pin) |
| { |
| const struct gpio_sam3_config *cfg = dev->config->config_info; |
| 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; |
| } |
| |
| cfg->port->PIO_IER |= mask; |
| |
| return 0; |
| } |
| |
| static int gpio_sam3_disable_callback(struct device *dev, |
| int access_op, u32_t pin) |
| { |
| const struct gpio_sam3_config *cfg = dev->config->config_info; |
| 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; |
| } |
| |
| cfg->port->PIO_IDR |= mask; |
| |
| return 0; |
| } |
| |
| static const struct gpio_driver_api gpio_sam3_drv_api_funcs = { |
| .config = gpio_sam3_config, |
| .write = gpio_sam3_write, |
| .read = gpio_sam3_read, |
| .manage_callback = gpio_sam3_manage_callback, |
| .enable_callback = gpio_sam3_enable_callback, |
| .disable_callback = gpio_sam3_disable_callback, |
| }; |
| |
| /** |
| * @brief Initialization function of MMIO |
| * |
| * @param dev Device struct |
| * @return 0 if successful, failed otherwise. |
| */ |
| static int gpio_sam3_init(struct device *dev) |
| { |
| const struct gpio_sam3_config *cfg = dev->config->config_info; |
| |
| cfg->config_func(dev); |
| |
| return 0; |
| } |
| |
| /* Port A */ |
| #ifdef CONFIG_GPIO_ATMEL_SAM3_PORTA |
| static void gpio_sam3_config_a(struct device *dev); |
| |
| static const struct gpio_sam3_config gpio_sam3_a_cfg = { |
| .port = PIOA, |
| |
| .config_func = gpio_sam3_config_a, |
| }; |
| |
| static struct gpio_sam3_runtime gpio_sam3_a_runtime; |
| |
| DEVICE_AND_API_INIT(gpio_sam3_a, CONFIG_GPIO_ATMEL_SAM3_PORTA_DEV_NAME, |
| gpio_sam3_init, &gpio_sam3_a_runtime, &gpio_sam3_a_cfg, |
| POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, |
| &gpio_sam3_drv_api_funcs); |
| |
| static void gpio_sam3_config_a(struct device *dev) |
| { |
| /* Enable clock for PIO controller */ |
| PMC->PMC_PCER0 = BIT(ID_PIOA); |
| |
| IRQ_CONNECT(PIOA_IRQn, CONFIG_GPIO_ATMEL_SAM3_PORTA_IRQ_PRI, |
| gpio_sam3_isr, DEVICE_GET(gpio_sam3_a), 0); |
| irq_enable(PIOA_IRQn); |
| } |
| #endif /* CONFIG_GPIO_ATMEL_SAM3_PORTA */ |
| |
| /* Port B */ |
| #ifdef CONFIG_GPIO_ATMEL_SAM3_PORTB |
| static void gpio_sam3_config_b(struct device *dev); |
| |
| static const struct gpio_sam3_config gpio_sam3_b_cfg = { |
| .port = PIOB, |
| |
| .config_func = gpio_sam3_config_b, |
| }; |
| |
| static struct gpio_sam3_runtime gpio_sam3_b_runtime; |
| |
| DEVICE_AND_API_INIT(gpio_sam3_b, CONFIG_GPIO_ATMEL_SAM3_PORTB_DEV_NAME, |
| gpio_sam3_init, &gpio_sam3_b_runtime, &gpio_sam3_b_cfg, |
| POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, |
| &gpio_sam3_drv_api_funcs); |
| |
| static void gpio_sam3_config_b(struct device *dev) |
| { |
| /* Enable clock for PIO controller */ |
| PMC->PMC_PCER0 = BIT(ID_PIOB); |
| |
| IRQ_CONNECT(PIOB_IRQn, CONFIG_GPIO_ATMEL_SAM3_PORTB_IRQ_PRI, |
| gpio_sam3_isr, DEVICE_GET(gpio_sam3_b), 0); |
| irq_enable(PIOB_IRQn); |
| } |
| #endif /* CONFIG_GPIO_ATMEL_SAM3_PORTB */ |
| |
| /* Port C */ |
| #ifdef CONFIG_GPIO_ATMEL_SAM3_PORTC |
| static void gpio_sam3_config_c(struct device *dev); |
| |
| static const struct gpio_sam3_config gpio_sam3_c_cfg = { |
| .port = PIOC, |
| |
| .config_func = gpio_sam3_config_c, |
| }; |
| |
| static struct gpio_sam3_runtime gpio_sam3_c_runtime; |
| |
| DEVICE_AND_API_INIT(gpio_sam3_c, CONFIG_GPIO_ATMEL_SAM3_PORTC_DEV_NAME, |
| gpio_sam3_init, &gpio_sam3_c_runtime, &gpio_sam3_c_cfg, |
| POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, |
| &gpio_sam3_drv_api_funcs); |
| |
| static void gpio_sam3_config_c(struct device *dev) |
| { |
| /* Enable clock for PIO controller */ |
| PMC->PMC_PCER0 = BIT(ID_PIOC); |
| |
| IRQ_CONNECT(PIOC_IRQn, CONFIG_GPIO_ATMEL_SAM3_PORTC_IRQ_PRI, |
| gpio_sam3_isr, DEVICE_GET(gpio_sam3_c), 0); |
| irq_enable(PIOC_IRQn); |
| } |
| #endif /* CONFIG_GPIO_ATMEL_SAM3_PORTA */ |
| |
| /* Port D */ |
| #ifdef CONFIG_GPIO_ATMEL_SAM3_PORTD |
| static void gpio_sam3_config_d(struct device *dev); |
| |
| static const struct gpio_sam3_config gpio_sam3_d_cfg = { |
| .port = PIOD, |
| |
| .config_func = gpio_sam3_config_d, |
| }; |
| |
| static struct gpio_sam3_runtime gpio_sam3_d_runtime; |
| |
| DEVICE_AND_API_INIT(gpio_sam3_d, CONFIG_GPIO_ATMEL_SAM3_PORTD_DEV_NAME, |
| gpio_sam3_init, &gpio_sam3_d_runtime, &gpio_sam3_d_cfg, |
| POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, |
| &gpio_sam3_drv_api_funcs); |
| |
| static void gpio_sam3_config_d(struct device *dev) |
| { |
| /* Enable clock for PIO controller */ |
| PMC->PMC_PCER0 = BIT(ID_PIOD); |
| |
| IRQ_CONNECT(PIOD_IRQn, CONFIG_GPIO_ATMEL_SAM3_PORTD_IRQ_PRI, |
| gpio_sam3_isr, DEVICE_GET(gpio_sam3_d), 0); |
| irq_enable(PIOD_IRQn); |
| } |
| #endif /* CONFIG_GPIO_ATMEL_SAM3_PORTD */ |