/* * Copyright (c) 2023, Intel Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT altr_pio_1_0 #include #include #include #include #define ALTERA_AVALON_PIO_DATA_OFFSET 0x00 #define ALTERA_AVALON_PIO_DIRECTION_OFFSET 0x04 #define ALTERA_AVALON_PIO_IRQ_OFFSET 0x08 #define ALTERA_AVALON_PIO_SET_BITS 0x10 #define ALTERA_AVALON_PIO_CLEAR_BITS 0x14 typedef void (*altera_cfg_func_t)(void); struct gpio_altera_config { struct gpio_driver_config common; uintptr_t reg_base; uint32_t irq_num; uint8_t direction; uint8_t outset; uint8_t outclear; altera_cfg_func_t cfg_func; }; struct gpio_altera_data { /* gpio_driver_data needs to be first */ struct gpio_driver_data common; /* list of callbacks */ sys_slist_t cb; struct k_spinlock lock; }; static bool gpio_pin_direction(const struct device *dev, uint32_t pin_mask) { const struct gpio_altera_config *cfg = dev->config; const int direction = cfg->direction; uintptr_t reg_base = cfg->reg_base; uint32_t addr; uint32_t pin_direction; if (pin_mask == 0) { return -EINVAL; } /* Check if the direction is Bidirectional */ if (direction != 0) { return -EINVAL; } addr = reg_base + ALTERA_AVALON_PIO_DIRECTION_OFFSET; pin_direction = sys_read32(addr); if (!(pin_direction & pin_mask)) { return false; } return true; } static int gpio_altera_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) { const struct gpio_altera_config *cfg = dev->config; struct gpio_altera_data * const data = dev->data; const int port_pin_mask = cfg->common.port_pin_mask; const int direction = cfg->direction; uintptr_t reg_base = cfg->reg_base; k_spinlock_key_t key; uint32_t addr; /* Check if pin number is within range */ if ((port_pin_mask & BIT(pin)) == 0) { return -EINVAL; } /* Check if the direction is Bidirectional */ if (direction != 0) { return -EINVAL; } addr = reg_base + ALTERA_AVALON_PIO_DIRECTION_OFFSET; key = k_spin_lock(&data->lock); if (flags == GPIO_INPUT) { sys_clear_bits(addr, BIT(pin)); } else if (flags == GPIO_OUTPUT) { sys_set_bits(addr, BIT(pin)); } else { return -EINVAL; } k_spin_unlock(&data->lock, key); return 0; } static int gpio_altera_port_get_raw(const struct device *dev, uint32_t *value) { const struct gpio_altera_config *cfg = dev->config; uintptr_t reg_base = cfg->reg_base; uint32_t addr; addr = reg_base + ALTERA_AVALON_PIO_DATA_OFFSET; if (value == NULL) { return -EINVAL; } *value = sys_read32((addr)); return 0; } static int gpio_altera_port_set_bits_raw(const struct device *dev, gpio_port_pins_t mask) { const struct gpio_altera_config *cfg = dev->config; struct gpio_altera_data * const data = dev->data; const uint8_t outset = cfg->outset; const int port_pin_mask = cfg->common.port_pin_mask; uintptr_t reg_base = cfg->reg_base; uint32_t addr; k_spinlock_key_t key; if ((port_pin_mask & mask) == 0) { return -EINVAL; } if (!gpio_pin_direction(dev, mask)) { return -EINVAL; } key = k_spin_lock(&data->lock); if (outset) { addr = reg_base + ALTERA_AVALON_PIO_SET_BITS; sys_write32(mask, addr); } else { addr = reg_base + ALTERA_AVALON_PIO_DATA_OFFSET; sys_set_bits(addr, mask); } k_spin_unlock(&data->lock, key); return 0; } static int gpio_altera_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t mask) { const struct gpio_altera_config *cfg = dev->config; struct gpio_altera_data * const data = dev->data; const uint8_t outclear = cfg->outclear; const int port_pin_mask = cfg->common.port_pin_mask; uintptr_t reg_base = cfg->reg_base; uint32_t addr; k_spinlock_key_t key; /* Check if mask range within 32 */ if ((port_pin_mask & mask) == 0) { return -EINVAL; } if (!gpio_pin_direction(dev, mask)) { return -EINVAL; } key = k_spin_lock(&data->lock); if (outclear) { addr = reg_base + ALTERA_AVALON_PIO_CLEAR_BITS; sys_write32(mask, addr); } else { addr = reg_base + ALTERA_AVALON_PIO_DATA_OFFSET; sys_clear_bits(addr, mask); } k_spin_unlock(&data->lock, key); return 0; } static int gpio_init(const struct device *dev) { const struct gpio_altera_config *cfg = dev->config; /* Configure GPIO device */ cfg->cfg_func(); return 0; } static int gpio_altera_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, enum gpio_int_mode mode, enum gpio_int_trig trig) { ARG_UNUSED(trig); const struct gpio_altera_config *cfg = dev->config; struct gpio_altera_data * const data = dev->data; uintptr_t reg_base = cfg->reg_base; const int port_pin_mask = cfg->common.port_pin_mask; uint32_t addr; k_spinlock_key_t key; /* Check if pin number is within range */ if ((port_pin_mask & BIT(pin)) == 0) { return -EINVAL; } if (!gpio_pin_direction(dev, BIT(pin))) { return -EINVAL; } addr = reg_base + ALTERA_AVALON_PIO_IRQ_OFFSET; key = k_spin_lock(&data->lock); switch (mode) { case GPIO_INT_MODE_DISABLED: /* Disable interrupt of pin */ sys_clear_bits(addr, BIT(pin)); irq_disable(cfg->irq_num); break; case GPIO_INT_MODE_LEVEL: case GPIO_INT_MODE_EDGE: /* Enable interrupt of pin */ sys_set_bits(addr, BIT(pin)); irq_enable(cfg->irq_num); break; default: return -EINVAL; } k_spin_unlock(&data->lock, key); return 0; } static int gpio_altera_manage_callback(const struct device *dev, struct gpio_callback *callback, bool set) { struct gpio_altera_data * const data = dev->data; return gpio_manage_callback(&data->cb, callback, set); } static void gpio_altera_irq_handler(const struct device *dev) { const struct gpio_altera_config *cfg = dev->config; struct gpio_altera_data *data = dev->data; uintptr_t reg_base = cfg->reg_base; uint32_t port_value; uint32_t addr; k_spinlock_key_t key; addr = reg_base + ALTERA_AVALON_PIO_IRQ_OFFSET; key = k_spin_lock(&data->lock); port_value = sys_read32(addr); sys_clear_bits(addr, port_value); k_spin_unlock(&data->lock, key); /* Call the corresponding callback registered for the pin */ gpio_fire_callbacks(&data->cb, dev, port_value); } static const struct gpio_driver_api gpio_altera_driver_api = { .pin_configure = gpio_altera_configure, .port_get_raw = gpio_altera_port_get_raw, .port_set_masked_raw = NULL, .port_set_bits_raw = gpio_altera_port_set_bits_raw, .port_clear_bits_raw = gpio_altera_port_clear_bits_raw, .port_toggle_bits = NULL, .pin_interrupt_configure = gpio_altera_pin_interrupt_configure, .manage_callback = gpio_altera_manage_callback }; #define GPIO_CFG_IRQ(idx, n) \ IRQ_CONNECT(DT_INST_IRQ_BY_IDX(n, idx, irq), \ COND_CODE_1(DT_INST_IRQ_HAS_CELL(n, priority), \ DT_INST_IRQ(n, priority), (0)), gpio_altera_irq_handler, \ DEVICE_DT_INST_GET(n), 0); \ #define CREATE_GPIO_DEVICE(n) \ static void gpio_altera_cfg_func_##n(void); \ static struct gpio_altera_data gpio_altera_data_##n; \ static struct gpio_altera_config gpio_config_##n = { \ .common = { \ .port_pin_mask = \ GPIO_PORT_PIN_MASK_FROM_DT_INST(n), \ }, \ .reg_base = DT_INST_REG_ADDR(n), \ .direction = DT_INST_ENUM_IDX(n, direction), \ .irq_num = COND_CODE_1(DT_INST_IRQ_HAS_IDX(n, 0), (DT_INST_IRQN(n)), (0)),\ .cfg_func = gpio_altera_cfg_func_##n, \ .outset = DT_INST_PROP(n, outset), \ .outclear = DT_INST_PROP(n, outclear), \ }; \ \ DEVICE_DT_INST_DEFINE(n, \ gpio_init, \ NULL, \ &gpio_altera_data_##n, \ &gpio_config_##n, \ POST_KERNEL, \ CONFIG_GPIO_INIT_PRIORITY, \ &gpio_altera_driver_api); \ \ static void gpio_altera_cfg_func_##n(void) \ { \ LISTIFY(DT_NUM_IRQS(DT_DRV_INST(n)), GPIO_CFG_IRQ, (), n)\ } DT_INST_FOREACH_STATUS_OKAY(CREATE_GPIO_DEVICE)