diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index 099bd0511fe..ae29fb9c311 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -29,6 +29,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_ESP32 gpio_esp32.c) zephyr_library_sources_ifdef(CONFIG_GPIO_FXL6408 gpio_fxl6408.c) zephyr_library_sources_ifdef(CONFIG_GPIO_GD32 gpio_gd32.c) zephyr_library_sources_ifdef(CONFIG_GPIO_GECKO gpio_gecko.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_GRGPIO2 gpio_grgpio2.c) zephyr_library_sources_ifdef(CONFIG_GPIO_IMX gpio_imx.c) zephyr_library_sources_ifdef(CONFIG_GPIO_INFINEON_CAT1 gpio_ifx_cat1.c) zephyr_library_sources_ifdef(CONFIG_GPIO_INTEL gpio_intel.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index ea56ed7a679..23660b95943 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -122,6 +122,7 @@ source "drivers/gpio/Kconfig.esp32" source "drivers/gpio/Kconfig.fxl6408" source "drivers/gpio/Kconfig.gd32" source "drivers/gpio/Kconfig.gecko" +source "drivers/gpio/Kconfig.grgpio" source "drivers/gpio/Kconfig.ifx_cat1" source "drivers/gpio/Kconfig.imx" source "drivers/gpio/Kconfig.intel" diff --git a/drivers/gpio/Kconfig.grgpio b/drivers/gpio/Kconfig.grgpio new file mode 100644 index 00000000000..118cb5c0224 --- /dev/null +++ b/drivers/gpio/Kconfig.grgpio @@ -0,0 +1,9 @@ +# Copyright (c) 2023 Frontgrade Gaisler AB +# SPDX-License-Identifier: Apache-2.0 + +config GPIO_GRGPIO2 + bool "GRLIB GRGPIO revision 2" + default y + depends on DT_HAS_GAISLER_GRGPIO_ENABLED + help + Enable driver for GRLIB GRGPIO revision 2. diff --git a/drivers/gpio/gpio_grgpio.h b/drivers/gpio/gpio_grgpio.h new file mode 100644 index 00000000000..6a3958f7a9e --- /dev/null +++ b/drivers/gpio/gpio_grgpio.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 Frontgrade Gaisler AB + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_DRIVERS_GPIO_GPIO_GRGPIO_H_ +#define ZEPHYR_DRIVERS_GPIO_GPIO_GRGPIO_H_ + +struct grgpio_regs { + uint32_t data; /* 0x00 I/O port data register */ + uint32_t output; /* 0x04 I/O port output register */ + uint32_t dir; /* 0x08 I/O port direction register */ + uint32_t imask; /* 0x0C Interrupt mask register */ + uint32_t ipol; /* 0x10 Interrupt polarity register */ + uint32_t iedge; /* 0x14 Interrupt edge register */ + uint32_t bypass; /* 0x18 Bypass register */ + uint32_t cap; /* 0x1C Capability register */ + uint32_t irqmap[4]; /* 0x20 - 0x2C Interrupt map registers */ + uint32_t res_30; /* 0x30 Reserved */ + uint32_t res_34; /* 0x34 Reserved */ + uint32_t res_38; /* 0x38 Reserved */ + uint32_t res_3C; /* 0x3C Reserved */ + uint32_t iavail; /* 0x40 Interrupt available register */ + uint32_t iflag; /* 0x44 Interrupt flag register */ + uint32_t res_48; /* 0x48 Reserved */ + uint32_t pulse; /* 0x4C Pulse register */ + uint32_t res_50; /* 0x50 Reserved */ + uint32_t output_or; /* 0x54 I/O port output register, logical-OR */ + uint32_t dir_or; /* 0x58 I/O port dir. register, logical-OR */ + uint32_t imask_or; /* 0x5C Interrupt mask register, logical-OR */ + uint32_t res_60; /* 0x60 Reserved */ + uint32_t output_and; /* 0x64 I/O port output register, logical-AND */ + uint32_t dir_and; /* 0x68 I/O port dir. register, logical-AND */ + uint32_t imask_and; /* 0x6C Interrupt mask register, logical-AND */ + uint32_t res_70; /* 0x70 Reserved */ + uint32_t output_xor; /* 0x74 I/O port output register, logical-XOR */ + uint32_t dir_xor; /* 0x78 I/O port dir. register, logical-XOR */ + uint32_t imask_xor; /* 0x7C Interrupt mask register, logical-XOR */ +}; + +#define GRGPIO_CAP_PU_BIT 18 +#define GRGPIO_CAP_IER_BIT 17 +#define GRGPIO_CAP_IFL_BIT 16 +#define GRGPIO_CAP_IRQGEN_BIT 8 +#define GRGPIO_CAP_NLINES_BIT 0 + +#define GRGPIO_CAP_PU (0x1 << GRGPIO_CAP_PU_BIT) +#define GRGPIO_CAP_IER (0x1 << GRGPIO_CAP_IER_BIT) +#define GRGPIO_CAP_IFL (0x1 << GRGPIO_CAP_IFL_BIT) +#define GRGPIO_CAP_IRQGEN (0x1f << GRGPIO_CAP_IRQGEN_BIT) +#define GRGPIO_CAP_NLINES (0x1f << GRGPIO_CAP_NLINES_BIT) + +#endif /* ZEPHYR_DRIVERS_GPIO_GPIO_GRGPIO_H_ */ diff --git a/drivers/gpio/gpio_grgpio2.c b/drivers/gpio/gpio_grgpio2.c new file mode 100644 index 00000000000..fbe57f0ce67 --- /dev/null +++ b/drivers/gpio/gpio_grgpio2.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2023 Frontgrade Gaisler AB + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Driver for GRLIB GRGPIO revision 2. + * - iflag determine pending interrupt. + * - interrupt map decides interrupt number if implemented. + * - logic or/and/xor registers used when possible + */ + +#define DT_DRV_COMPAT gaisler_grgpio + +#include +#include +#include +#include "gpio_grgpio.h" + +#define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL +#include +LOG_MODULE_REGISTER(gpio_grgpio2); + +struct cfg { + struct gpio_driver_config common; + volatile struct grgpio_regs *regs; + int interrupt; +}; + +struct data { + struct gpio_driver_data common; + struct k_spinlock lock; + sys_slist_t cb; + uint32_t imask; + uint32_t connected; + int irqgen; +}; + +static void grgpio_isr(const struct device *dev); + +static int pin_configure(const struct device *dev, + gpio_pin_t pin, gpio_flags_t flags) +{ + const struct cfg *cfg = dev->config; + struct data *data = dev->data; + volatile struct grgpio_regs *regs = cfg->regs; + uint32_t mask = 1 << pin; + + if (flags & GPIO_SINGLE_ENDED) { + return -ENOTSUP; + } + + if (flags == GPIO_DISCONNECTED) { + return -ENOTSUP; + } + + if ((flags & GPIO_DIR_MASK) == (GPIO_INPUT | GPIO_OUTPUT)) { + return -ENOTSUP; + } + + if ((flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0) { + return -ENOTSUP; + } + + if (flags & GPIO_OUTPUT) { + k_spinlock_key_t key; + + /* + * Register operations are atomic, but do the sequence under + * lock so it serializes. + */ + key = k_spin_lock(&data->lock); + if (flags & GPIO_OUTPUT_INIT_HIGH) { + regs->output_or = mask; + } else if (flags & GPIO_OUTPUT_INIT_LOW) { + regs->output_and = ~mask; + } + regs->dir_or = mask; + k_spin_unlock(&data->lock, key); + } else { + regs->dir_and = ~mask; + } + + return 0; +} + +static int port_get_raw(const struct device *dev, gpio_port_value_t *value) +{ + const struct cfg *cfg = dev->config; + + *value = cfg->regs->data; + return 0; +} + +static int port_set_masked_raw(const struct device *dev, + gpio_port_pins_t mask, + gpio_port_value_t value) +{ + const struct cfg *cfg = dev->config; + struct data *data = dev->data; + volatile struct grgpio_regs *regs = cfg->regs; + uint32_t port_val; + k_spinlock_key_t key; + + value &= mask; + key = k_spin_lock(&data->lock); + port_val = (regs->output & ~mask) | value; + regs->output = port_val; + k_spin_unlock(&data->lock, key); + + return 0; +} + +static int port_set_bits_raw(const struct device *dev, gpio_port_pins_t pins) +{ + const struct cfg *cfg = dev->config; + volatile struct grgpio_regs *regs = cfg->regs; + + regs->output_or = pins; + return 0; +} + +static int port_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins) +{ + const struct cfg *cfg = dev->config; + volatile struct grgpio_regs *regs = cfg->regs; + + regs->output_and = ~pins; + return 0; +} + +static int port_toggle_bits(const struct device *dev, gpio_port_pins_t pins) +{ + const struct cfg *cfg = dev->config; + volatile struct grgpio_regs *regs = cfg->regs; + + regs->output_xor = pins; + return 0; +} + +static uint32_t get_pending_int(const struct device *dev) +{ + const struct cfg *cfg = dev->config; + volatile struct grgpio_regs *regs = cfg->regs; + + return regs->iflag; +} + +static int pin_interrupt_configure(const struct device *dev, + gpio_pin_t pin, + enum gpio_int_mode mode, + enum gpio_int_trig trig) +{ + const struct cfg *cfg = dev->config; + struct data *data = dev->data; + volatile struct grgpio_regs *regs = cfg->regs; + int ret = 0; + const uint32_t mask = 1 << pin; + uint32_t polmask; + k_spinlock_key_t key; + + if ((mask & data->imask) == 0) { + /* This pin can not generate interrupt */ + return -ENOTSUP; + } + if (mode != GPIO_INT_MODE_DISABLED) { + if (trig == GPIO_INT_TRIG_LOW) { + polmask = 0; + } else if (trig == GPIO_INT_TRIG_HIGH) { + polmask = mask; + } else { + return -ENOTSUP; + } + } + key = k_spin_lock(&data->lock); + if (mode == GPIO_INT_MODE_DISABLED) { + regs->imask_and = ~mask; + } else if (mode == GPIO_INT_MODE_LEVEL) { + regs->imask_and = ~mask; + regs->iedge &= ~mask; + regs->ipol = (regs->ipol & ~mask) | polmask; + regs->imask_or = mask; + } else if (mode == GPIO_INT_MODE_EDGE) { + regs->imask_and = ~mask; + regs->iedge |= mask; + regs->ipol = (regs->ipol & ~mask) | polmask; + regs->imask_or = mask; + } else { + ret = -ENOTSUP; + } + k_spin_unlock(&data->lock, key); + + /* Remove old interrupt history for this pin. */ + regs->iflag = mask; + + int interrupt = cfg->interrupt; + const int irqgen = data->irqgen; + + if (irqgen == 0) { + interrupt += pin; + } else if (irqgen == 1) { + ; + } else if (irqgen < 32) { + /* look up interrupt number in GRGPIO interrupt map */ + uint32_t val = regs->irqmap[pin/4]; + + val >>= (3 - pin % 4) * 8; + interrupt += (val & 0x1f); + } + + if (interrupt && ((1 << interrupt) & data->connected) == 0) { + irq_connect_dynamic( + interrupt, + 0, + (void (*)(const void *)) grgpio_isr, + dev, + 0 + ); + irq_enable(interrupt); + data->connected |= 1 << interrupt; + } + + return ret; +} + +static int manage_callback(const struct device *dev, + struct gpio_callback *callback, + bool set) +{ + struct data *data = dev->data; + + return gpio_manage_callback(&data->cb, callback, set); +} + +static void grgpio_isr(const struct device *dev) +{ + const struct cfg *cfg = dev->config; + struct data *data = dev->data; + volatile struct grgpio_regs *regs = cfg->regs; + uint32_t pins; + + /* no locking needed when iflag is implemented */ + pins = regs->iflag; + if (pins == 0) { + return; + } + regs->iflag = pins; + gpio_fire_callbacks(&data->cb, dev, pins); +} + +static int grgpio_init(const struct device *dev) +{ + const struct cfg *cfg = dev->config; + struct data *data = dev->data; + volatile struct grgpio_regs *regs = cfg->regs; + + data->irqgen = (regs->cap & GRGPIO_CAP_IRQGEN) >> GRGPIO_CAP_IRQGEN_BIT; + regs->dir = 0; + /* Mask all Interrupts */ + regs->imask = 0; + /* Make IRQ Rising edge triggered default */ + regs->ipol = 0xffffffff; + regs->iedge = 0xffffffff; + regs->iflag = 0xffffffff; + /* Read what I/O lines have IRQ support */ + data->imask = regs->ipol; + + return 0; +} + +static const struct gpio_driver_api driver_api = { + .pin_configure = pin_configure, + .port_get_raw = port_get_raw, + .port_set_masked_raw = port_set_masked_raw, + .port_set_bits_raw = port_set_bits_raw, + .port_clear_bits_raw = port_clear_bits_raw, + .port_toggle_bits = port_toggle_bits, + .pin_interrupt_configure = pin_interrupt_configure, + .manage_callback = manage_callback, + .get_pending_int = get_pending_int, +}; + +#define GRGPIO_INIT(n) \ + static const struct cfg cfg_##n = { \ + .common = { \ + .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n),\ + }, \ + .regs = (void *) DT_INST_REG_ADDR(n), \ + .interrupt = DT_INST_IRQN(n), \ + }; \ + static struct data data_##n; \ + \ + DEVICE_DT_INST_DEFINE(n, \ + grgpio_init, \ + NULL, \ + &data_##n, \ + &cfg_##n, \ + POST_KERNEL, \ + CONFIG_GPIO_INIT_PRIORITY, \ + &driver_api \ + ); + +DT_INST_FOREACH_STATUS_OKAY(GRGPIO_INIT) diff --git a/dts/bindings/gpio/gaisler,grgpio.yaml b/dts/bindings/gpio/gaisler,grgpio.yaml new file mode 100644 index 00000000000..7c44e323c1d --- /dev/null +++ b/dts/bindings/gpio/gaisler,grgpio.yaml @@ -0,0 +1,16 @@ +description: Gaisler GRLIB GRGPIO - General Purpose I/O Port + +compatible: "gaisler,grgpio" + +include: [gpio-controller.yaml, base.yaml] + +properties: + reg: + required: true + + "#gpio-cells": + const: 2 + +gpio-cells: + - pin + - flags