/* * Copyright (c) 2018 Justin Watson * Copyright (c) 2020 Gerson Fernando Budke * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT atmel_sam4l_gpio #include #include #include #include #include #include #include "gpio_utils.h" typedef void (*config_func_t)(const struct device *dev); struct gpio_sam_config { /* gpio_driver_config needs to be first */ struct gpio_driver_config common; Gpio *regs; config_func_t config_func; uint32_t periph_id; }; struct gpio_sam_runtime { /* gpio_driver_data needs to be first */ struct gpio_driver_data common; sys_slist_t cb; }; #define DEV_CFG(dev) \ ((const struct gpio_sam_config * const)(dev)->config) #define DEV_DATA(dev) \ ((struct gpio_sam_runtime * const)(dev)->data) #define GPIO_SAM_ALL_PINS 0xFFFFFFFF static int gpio_sam_port_configure(const struct device *dev, uint32_t mask, gpio_flags_t flags) { const struct gpio_sam_config * const cfg = DEV_CFG(dev); Gpio * const gpio = cfg->regs; /* No hardware support */ if (flags & GPIO_SINGLE_ENDED) { return -ENOTSUP; } if (!(flags & (GPIO_OUTPUT | GPIO_INPUT))) { gpio->IERC = mask; gpio->PUERC = mask; gpio->PDERC = mask; gpio->GPERS = mask; gpio->ODERC = mask; gpio->STERC = mask; return 0; } /* * Always enable schmitt-trigger because SAM4L GPIO Ctrl * is Input only or Input/Output. */ gpio->STERS = mask; if (flags & GPIO_OUTPUT) { if (flags & GPIO_OUTPUT_INIT_HIGH) { gpio->OVRS = mask; } if (flags & GPIO_OUTPUT_INIT_LOW) { gpio->OVRC = mask; } gpio->ODERS = mask; } else { gpio->ODERC = mask; } gpio->PUERC = mask; gpio->PDERC = mask; if (flags & GPIO_PULL_UP) { gpio->PUERS = mask; } else if (flags & GPIO_PULL_DOWN) { gpio->PDERS = mask; } /* Enable the GPIO to control the pin (instead of a peripheral). */ gpio->GPERS = mask; return 0; } static int gpio_sam_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) { return gpio_sam_port_configure(dev, BIT(pin), flags); } static int gpio_sam_port_get_raw(const struct device *dev, uint32_t *value) { const struct gpio_sam_config * const cfg = DEV_CFG(dev); Gpio * const gpio = cfg->regs; *value = gpio->PVR; return 0; } static int gpio_sam_port_set_masked_raw(const struct device *dev, uint32_t mask, uint32_t value) { const struct gpio_sam_config * const cfg = DEV_CFG(dev); Gpio * const gpio = cfg->regs; gpio->OVR = (gpio->PVR & ~mask) | (mask & value); return 0; } static int gpio_sam_port_set_bits_raw(const struct device *dev, uint32_t mask) { const struct gpio_sam_config * const cfg = DEV_CFG(dev); Gpio * const gpio = cfg->regs; gpio->OVRS = mask; return 0; } static int gpio_sam_port_clear_bits_raw(const struct device *dev, uint32_t mask) { const struct gpio_sam_config * const cfg = DEV_CFG(dev); Gpio * const gpio = cfg->regs; gpio->OVRC = mask; return 0; } static int gpio_sam_port_toggle_bits(const struct device *dev, uint32_t mask) { const struct gpio_sam_config * const cfg = DEV_CFG(dev); Gpio * const gpio = cfg->regs; gpio->OVRT = mask; return 0; } static int gpio_sam_port_interrupt_configure(const struct device *dev, uint32_t mask, enum gpio_int_mode mode, enum gpio_int_trig trig) { const struct gpio_sam_config * const cfg = DEV_CFG(dev); Gpio * const gpio = cfg->regs; if (mode == GPIO_INT_MODE_LEVEL) { return -ENOTSUP; } gpio->IERC = mask; gpio->IMR0C = mask; gpio->IMR1C = mask; if (trig != GPIO_INT_TRIG_BOTH) { if (trig == GPIO_INT_TRIG_HIGH) { gpio->IMR0S = mask; } else { gpio->IMR1S = mask; } } if (mode != GPIO_INT_MODE_DISABLED) { gpio->IFRC = mask; gpio->IERS = mask; } return 0; } static int gpio_sam_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, enum gpio_int_mode mode, enum gpio_int_trig trig) { return gpio_sam_port_interrupt_configure(dev, BIT(pin), mode, trig); } static void gpio_sam_isr(const struct device *dev) { const struct gpio_sam_config * const cfg = DEV_CFG(dev); Gpio * const gpio = cfg->regs; struct gpio_sam_runtime *context = dev->data; uint32_t int_stat; int_stat = gpio->IFR; gpio->IFRC = int_stat; gpio_fire_callbacks(&context->cb, dev, int_stat); } static int gpio_sam_manage_callback(const struct device *port, struct gpio_callback *callback, bool set) { struct gpio_sam_runtime *context = port->data; return gpio_manage_callback(&context->cb, callback, set); } static const struct gpio_driver_api gpio_sam_api = { .pin_configure = gpio_sam_config, .port_get_raw = gpio_sam_port_get_raw, .port_set_masked_raw = gpio_sam_port_set_masked_raw, .port_set_bits_raw = gpio_sam_port_set_bits_raw, .port_clear_bits_raw = gpio_sam_port_clear_bits_raw, .port_toggle_bits = gpio_sam_port_toggle_bits, .pin_interrupt_configure = gpio_sam_pin_interrupt_configure, .manage_callback = gpio_sam_manage_callback, }; int gpio_sam_init(const struct device *dev) { const struct gpio_sam_config * const cfg = DEV_CFG(dev); soc_pmc_peripheral_enable(cfg->periph_id); cfg->config_func(dev); return 0; } #define GPIO_SAM_IRQ_CONNECT(n, m) \ do { \ IRQ_CONNECT(DT_INST_IRQ_BY_IDX(n, m, irq), \ DT_INST_IRQ_BY_IDX(n, m, priority), \ gpio_sam_isr, \ DEVICE_DT_INST_GET(n), 0); \ irq_enable(DT_INST_IRQ_BY_IDX(n, m, irq)); \ } while (0) #define GPIO_SAM_INIT(n) \ static void port_##n##_sam_config_func(const struct device *dev);\ \ static const struct gpio_sam_config port_##n##_sam_config = { \ .common = { \ .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n),\ }, \ .regs = (Gpio *)DT_INST_REG_ADDR(n), \ .periph_id = DT_INST_PROP(n, peripheral_id), \ .config_func = port_##n##_sam_config_func, \ }; \ \ static struct gpio_sam_runtime port_##n##_sam_runtime; \ \ DEVICE_DT_INST_DEFINE(n, gpio_sam_init, NULL, \ &port_##n##_sam_runtime, \ &port_##n##_sam_config, POST_KERNEL, \ CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ &gpio_sam_api); \ \ static void port_##n##_sam_config_func(const struct device *dev)\ { \ GPIO_SAM_IRQ_CONNECT(n, 0); \ GPIO_SAM_IRQ_CONNECT(n, 1); \ GPIO_SAM_IRQ_CONNECT(n, 2); \ GPIO_SAM_IRQ_CONNECT(n, 3); \ } DT_INST_FOREACH_STATUS_OKAY(GPIO_SAM_INIT)