From 752f00c9598433399a2cc22791fcb5854eb6ea9c Mon Sep 17 00:00:00 2001 From: Gerson Fernando Budke Date: Sun, 23 Feb 2020 14:44:55 -0300 Subject: [PATCH] drivers: gpio: sam: Add sam4l SoC support Add initial version of SAM4L GPIO driver. Signed-off-by: Gerson Fernando Budke --- boards/arm/sam4l_ek/sam4l_ek_defconfig | 1 + drivers/gpio/CMakeLists.txt | 1 + drivers/gpio/Kconfig.sam | 10 +- drivers/gpio/gpio_sam4l.c | 281 ++++++++++++++++++ .../atmel_sam/sam4l/Kconfig.defconfig.series | 4 + 5 files changed, 296 insertions(+), 1 deletion(-) create mode 100644 drivers/gpio/gpio_sam4l.c diff --git a/boards/arm/sam4l_ek/sam4l_ek_defconfig b/boards/arm/sam4l_ek/sam4l_ek_defconfig index fd2c0e7ceb9..11e6258fed0 100644 --- a/boards/arm/sam4l_ek/sam4l_ek_defconfig +++ b/boards/arm/sam4l_ek/sam4l_ek_defconfig @@ -10,6 +10,7 @@ CONFIG_SERIAL=y CONFIG_BOARD_SAM4L_EK=y CONFIG_WATCHDOG=n CONFIG_SPI=y +CONFIG_GPIO=y # Enable HW stack protection CONFIG_HW_STACK_PROTECTION=y diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index ff30e545115..55dba3dbc78 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -21,6 +21,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_PCA95XX gpio_pca95xx.c) zephyr_library_sources_ifdef(CONFIG_GPIO_STM32 gpio_stm32.c) zephyr_library_sources_ifdef(CONFIG_GPIO_SAM0 gpio_sam0.c) zephyr_library_sources_ifdef(CONFIG_GPIO_SAM gpio_sam.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_SAM4L gpio_sam4l.c) zephyr_library_sources_ifdef(CONFIG_GPIO_SX1509B gpio_sx1509b.c) zephyr_library_sources_ifdef(CONFIG_GPIO_INTEL_APL gpio_intel_apl.c) zephyr_library_sources_ifdef(CONFIG_GPIO_STELLARIS gpio_stellaris.c) diff --git a/drivers/gpio/Kconfig.sam b/drivers/gpio/Kconfig.sam index 3f467a5a303..02d5bfc292c 100644 --- a/drivers/gpio/Kconfig.sam +++ b/drivers/gpio/Kconfig.sam @@ -1,11 +1,19 @@ # Atmel SAM GPIO configuration options # Copyright (c) 2018 Justin Watson +# Copyright (c) 2020 Gerson Fernando Budke # SPDX-License-Identifier: Apache-2.0 config GPIO_SAM bool "Atmel SAM GPIO (PORT) driver" default y - depends on SOC_FAMILY_SAM + depends on SOC_FAMILY_SAM && !SOC_SERIES_SAM4L help Enable support for the Atmel SAM 'PORT' GPIO controllers. + +config GPIO_SAM4L + bool "Atmel SAM4L GPIO (PORT) driver" + default y + depends on SOC_SERIES_SAM4L + help + Enable support for the Atmel SAM4L 'PORT' GPIO controllers. diff --git a/drivers/gpio/gpio_sam4l.c b/drivers/gpio/gpio_sam4l.c new file mode 100644 index 00000000000..8ff29eaabbf --- /dev/null +++ b/drivers/gpio/gpio_sam4l.c @@ -0,0 +1,281 @@ +/* + * 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_GET(port_##n##_sam), 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_AND_API_INIT(port_##n##_sam, DT_INST_LABEL(n), \ + gpio_sam_init, &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) diff --git a/soc/arm/atmel_sam/sam4l/Kconfig.defconfig.series b/soc/arm/atmel_sam/sam4l/Kconfig.defconfig.series index f13f83825ff..234e9516843 100644 --- a/soc/arm/atmel_sam/sam4l/Kconfig.defconfig.series +++ b/soc/arm/atmel_sam/sam4l/Kconfig.defconfig.series @@ -48,6 +48,10 @@ config I2C_SAM_TWIM default y depends on I2C +config GPIO_SAM4L + default y + depends on GPIO + config SPI_SAM default y depends on SPI