/* * 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)