Browse Source

drivers/gpio: Add support for GRLIB GRGPIO2

This adds support for the GRLIB GRGPIO2 controller used in
LEON and NOEL-V systems.

Signed-off-by: Martin Åberg <martin.aberg@gaisler.com>
pull/76436/head
Martin Åberg 4 years ago committed by Alberto Escolar
parent
commit
7dbc5f09ed
  1. 1
      drivers/gpio/CMakeLists.txt
  2. 1
      drivers/gpio/Kconfig
  3. 9
      drivers/gpio/Kconfig.grgpio
  4. 53
      drivers/gpio/gpio_grgpio.h
  5. 304
      drivers/gpio/gpio_grgpio2.c
  6. 16
      dts/bindings/gpio/gaisler,grgpio.yaml

1
drivers/gpio/CMakeLists.txt

@ -29,6 +29,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_ESP32 gpio_esp32.c) @@ -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)

1
drivers/gpio/Kconfig

@ -122,6 +122,7 @@ source "drivers/gpio/Kconfig.esp32" @@ -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"

9
drivers/gpio/Kconfig.grgpio

@ -0,0 +1,9 @@ @@ -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.

53
drivers/gpio/gpio_grgpio.h

@ -0,0 +1,53 @@ @@ -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_ */

304
drivers/gpio/gpio_grgpio2.c

@ -0,0 +1,304 @@ @@ -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 <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/gpio/gpio_utils.h>
#include "gpio_grgpio.h"
#define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL
#include <zephyr/logging/log.h>
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)

16
dts/bindings/gpio/gaisler,grgpio.yaml

@ -0,0 +1,16 @@ @@ -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
Loading…
Cancel
Save