diff --git a/arch/risc-v/include/litex/irq.h b/arch/risc-v/include/litex/irq.h index 1bfef0b5d43..652d9e870cc 100644 --- a/arch/risc-v/include/litex/irq.h +++ b/arch/risc-v/include/litex/irq.h @@ -39,10 +39,28 @@ #define LITEX_IRQ_TIMER0 (RISCV_IRQ_MEXT + 2) #define LITEX_IRQ_ETHMAC (RISCV_IRQ_MEXT + 3) #define LITEX_IRQ_SDCARD (RISCV_IRQ_MEXT + 4) +#define LITEX_IRQ_GPIO (RISCV_IRQ_MEXT + 5) + +/* The last hardware IRQ number */ + +#define LITEX_IRQ_LAST (LITEX_IRQ_GPIO) + +/* Second level GPIO interrupts. GPIO interrupts are decoded and dispatched + * as a second level of decoding: The first level dispatches to the GPIO + * interrupt handler. The second to the decoded GPIO interrupt handler. + */ + +#ifdef CONFIG_LITEX_GPIO_IRQ +# define LITEX_NIRQ_GPIO 32 +# define LITEX_FIRST_GPIOIRQ (LITEX_IRQ_LAST + 1) +# define LITEX_LAST_GPIOIRQ (LITES_FIRST_GPIOIRQ + LITEX_NIRQ_GPIO) +#else +# define LITEX_NIRQ_GPIO 0 +#endif /* Total number of IRQs */ -#define NR_IRQS (LITEX_IRQ_SDCARD + 1) +#define NR_IRQS (LITEX_IRQ_LAST + LITEX_NIRQ_GPIO + 1) #endif /* CONFIG_LITEX_USE_CUSTOM_IRQ_DEFINITIONS */ #endif /* __ARCH_RISCV_INCLUDE_LITEX_IRQ_H */ diff --git a/arch/risc-v/src/litex/Kconfig b/arch/risc-v/src/litex/Kconfig index 99472a94001..f3a04ed4eec 100644 --- a/arch/risc-v/src/litex/Kconfig +++ b/arch/risc-v/src/litex/Kconfig @@ -54,6 +54,23 @@ config LITEX_CUSTOM_IRQ_DEFINITIONS_PATH endif +config LITEX_GPIO + bool "GPIO" + default n + ---help--- + Select if gateware configuration has a GPIO interface available. + + +if LITEX_GPIO + +config LITEX_GPIO_IRQ + bool "Enable GPIO IRQ" + default n + ---help--- + Select if gateware configuration has support for GPIO IRQ events. + +endif + config LITEX_HAVE_UART0 bool default y diff --git a/arch/risc-v/src/litex/Make.defs b/arch/risc-v/src/litex/Make.defs index 5827658d93a..fc72c9c1454 100644 --- a/arch/risc-v/src/litex/Make.defs +++ b/arch/risc-v/src/litex/Make.defs @@ -31,6 +31,11 @@ CHIP_CSRCS += litex_lowputc.c litex_serial.c CHIP_CSRCS += litex_start.c litex_timerisr.c CHIP_ASRCS += litex_cache.S + +ifeq ($(CONFIG_LITEX_GPIO),y) +CHIP_CSRCS += litex_gpio.c +endif + ifeq ($(CONFIG_LITEX_SDIO),y) CHIP_CSRCS += litex_sdio.c endif diff --git a/arch/risc-v/src/litex/hardware/litex_memorymap.h b/arch/risc-v/src/litex/hardware/litex_memorymap.h index 1aeb7073b13..3cf0fbe2320 100644 --- a/arch/risc-v/src/litex/hardware/litex_memorymap.h +++ b/arch/risc-v/src/litex/hardware/litex_memorymap.h @@ -50,6 +50,20 @@ #define LITEX_TIMER0_BASE 0xf0006000 #define LITEX_UART0_BASE 0xf0006800 +/* GPIO peripheral definitions. + * - LITEX_GPIO_BASE is the first 32-bit address which contains a block + * of GPIO registers (peripheral). Each block can contain up to 32 pins. + * - LITEX_GPIO_OFFSET is the number of bytes between each GPIO peripheral. + * - LITEX_GPIO_MAX is the number of peripherals enabled in gateware. + * + * Each peripheral is referenced by an index in the GPIO driver. E.g Index 0 + * is the first GPIO peripheral at 0xf0008000. Index 1 is at 0xf00080020. + */ + +#define LITEX_GPIO_BASE 0xf0008000 +#define LITEX_GPIO_OFFSET 0x00000020 +#define LITEX_GPIO_MAX 8 + #define LITEX_ETHMAC_RXBASE 0x80000000 #define LITEX_ETHMAC_TXBASE 0x80001000 diff --git a/arch/risc-v/src/litex/litex_gpio.c b/arch/risc-v/src/litex/litex_gpio.c new file mode 100644 index 00000000000..e419a8e5727 --- /dev/null +++ b/arch/risc-v/src/litex/litex_gpio.c @@ -0,0 +1,358 @@ +/**************************************************************************** + * arch/risc-v/src/litex/litex_gpio.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include /* To access ffs() */ +#include +#include + +#include +#include +#include + +#include "riscv_internal.h" +#include "litex_gpio.h" +#include "litex_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define GPIO_REG_OE_OFFSET 0x00 +#define GPIO_REG_IN_OFFSET 0x04 +#define GPIO_REG_OUT_OFFSET 0x08 +#define GPIO_REG_MODE_OFFSET 0x0C +#define GPIO_REG_EDGE_OFFSET 0x10 +#define GPIO_REG_EV_STATUS_OFFSET 0x14 +#define GPIO_REG_EV_PEND_OFFSET 0x18 +#define GPIO_REG_EV_ENABLE_OFFSET 0x1C + +#ifdef CONFIG_LITEX_GPIO_IRQ +/* Helper to calculate the GPIO port index from an IRQ number. */ +#define irq_to_gpio_index(_irqno) (( _irqno - LITEX_IRQ_GPIO) % 32) + +/* Helper to calculate the extended IRQ number from GPIO port index. */ +#define gpio_index_to_irq(_i, _p) ((_i * 32) + _p + LITEX_FIRST_GPIOIRQ) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Functions Prototypes + ****************************************************************************/ + +static uint32_t gpiobase_at(uint32_t index); + +#ifdef CONFIG_LITEX_GPIO_IRQ +static void gpio_dispatch(int irq, uint32_t gpiobase, uint32_t * regs); +static int litex_gpio_interrupt(int irq, void *context, void * arg); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* A lookup table to keep track of all the base addresses for all gateware + * defined GPIO peripherals. This saves calculating the actual base address + * in the ISR routines, as there is only an IRQ number available at that + * point. + * + * The actual memory addresses are populated the first time it is used. + */ + +static uint32_t g_gpio_base[LITEX_GPIO_MAX] = +{ + 0 +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint32_t gpiobase_at(uint32_t index) +{ + DEBUGASSERT(index < LITEX_GPIO_MAX); + if (g_gpio_base[0] == 0) + { + for (int i = 0; i < LITEX_GPIO_MAX; i++) + { + g_gpio_base[i] = LITEX_GPIO_BASE + + (LITEX_GPIO_OFFSET * i); + } + } + + return g_gpio_base[index]; +} + +#ifdef CONFIG_LITEX_GPIO_IRQ +static void gpio_dispatch(int irq, uint32_t gpiobase, uint32_t * regs) +{ + int i; + int ndx = 0; + uint32_t bitmask; + + /* Query which pin interrupts are enabled */ + + uint32_t enabled = getreg32(gpiobase + GPIO_REG_EV_ENABLE_OFFSET); + + /* Query which interrupts are pending */ + + uint32_t pending = getreg32(gpiobase + GPIO_REG_EV_PEND_OFFSET); + + while ((i = ffs(pending))) + { + ndx += i; + bitmask = 0x1 << (ndx - 1); + + /* Clear the pending interrupt */ + + modifyreg32(gpiobase + GPIO_REG_EV_PEND_OFFSET, bitmask, bitmask); + + /* Dispatch the appropriate interrupt handler if the ISR is enabled. */ + + if (enabled & bitmask) + { + irq_dispatch(irq + ndx - 1, regs); + } + + pending >>= i; + } +} +#endif + +#ifdef CONFIG_LITEX_GPIO_IRQ +static int litex_gpio_interrupt(int irq, void * context, void * arg) +{ + uint32_t gpiobase = 0; + uint32_t gpioindex = irq_to_gpio_index(irq); + + DEBUGASSERT(gpioindex < LITEX_GPIO_MAX); + + gpiobase = gpiobase_at(gpioindex); + + gpio_dispatch(LITEX_FIRST_GPIOIRQ, gpiobase, (uint32_t *)context); + return OK; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: litex_gpio_config + * + * Description: + * Configure a GPIO pin based on encoded pin attributes. GPIO's currently + * only support a single attribute. + * + * Input Parameters: + * port - The GPIO port number to read from. + * pin - The pin, or bit offset inside the GPIO port. + * attr - The pin attribute to apply to the pin. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int litex_gpio_config(uint32_t port, uint32_t pin, gpio_pinattr_t attr) +{ + int ret = OK; + int bitmask = 0x1 << pin; + switch (attr) + { + case GPIO_PINMODE_INPUT: + modifyreg32(gpiobase_at(port) + GPIO_REG_OE_OFFSET, bitmask, 0); + break; + case GPIO_PINMODE_OUTPUT: + modifyreg32(gpiobase_at(port) + GPIO_REG_OE_OFFSET, bitmask, bitmask); + break; + default: + ret = -ENODEV; + break; + } + + return ret; +} + +/**************************************************************************** + * Name: litex_gpio_write + * + * Description: + * Write one or zero to the selected GPIO pin. + * + * Input Parameters: + * port - The GPIO port number to read from. + * pin - The pin, or bit offset inside the GPIO port. + * + ****************************************************************************/ + +void litex_gpio_write(uint32_t port, uint32_t pin, bool value) +{ + modifyreg32(gpiobase_at(port) + GPIO_REG_OUT_OFFSET, 0x1 << pin, + (uint32_t)value << pin); +} + +/**************************************************************************** + * Name: litex_gpio_read + * + * Description: + * Read one or zero from the selected GPIO pin. + * + * Input Parameters: + * port - The GPIO port number to read from. + * pin - The pin, or bit offset inside the GPIO port. + * + * Returned Value: + * True if the pin is logic high. False if the pin is logic low. + * + ****************************************************************************/ + +bool litex_gpio_read(uint32_t port, uint32_t pin) +{ + return (getreg32(gpiobase_at(port) + GPIO_REG_IN_OFFSET) >> pin) & 0x1; +} + +/**************************************************************************** + * Name: litex_gpio_irq_enable + * + * Description: + * Initialize logic to support a second level of interrupt decoding for + * GPIO pins on the GPIO port. + * + * Input Parameters: + * irq - The IRQ number to enable. Must be a hardware interrupt + * number as defined in irq.h. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_LITEX_GPIO_IRQ +int litex_gpio_irq_enable(int irq) +{ + int ret; + up_disable_irq(irq); + ret = irq_attach(irq, litex_gpio_interrupt, NULL); + if (ret == OK) + { + up_enable_irq(irq); + } + + return ret; +} +#endif + +/**************************************************************************** + * Name: litex_gpio_irq_configure + * + * Description: + * Enable the interrupt for specified GPIO IRQ. + * + * Input Parameters: + * config - The interrupt configuration. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + ****************************************************************************/ + +#ifdef CONFIG_LITEX_GPIO_IRQ +int litex_gpio_irq_config(struct gpio_isr_config_s * config) +{ + int bitmask = 0x1 << config->pin; + int irq = gpio_index_to_irq(config->port, config->pin); + int ret = OK; + uint32_t gpiobase = gpiobase_at(config->port); + + if (config->type & GPIO_ISR_NONE) + { + modifyreg32(gpiobase + GPIO_REG_EV_PEND_OFFSET, bitmask, bitmask); + modifyreg32(gpiobase + GPIO_REG_EV_ENABLE_OFFSET, bitmask, 0); + return OK; + } + + if (config->handler) + { + ret = irq_attach(irq, config->handler, config->userdata); + } + + if (ret != OK) + { + return ret; + } + +/* There are two registers which control the event type. + * - MODE: 0 sets single edge, 1 sets both edges. + * - EDGE: (only when mode = 0) 0 sets rising edge, 0 sets falling edge. + */ + + if (config->type & GPIO_ISR_BOTH_EDGES) + { + modifyreg32(gpiobase + GPIO_REG_MODE_OFFSET, bitmask, bitmask); + } + else + { + modifyreg32(gpiobase + GPIO_REG_MODE_OFFSET, bitmask, 0); + modifyreg32(gpiobase + GPIO_REG_EDGE_OFFSET, bitmask, + (config->type & GPIO_ISR_FALLING_EDGE) ? bitmask : 0); + } + + modifyreg32(gpiobase + GPIO_REG_EV_PEND_OFFSET, bitmask, bitmask); + modifyreg32(gpiobase + GPIO_REG_EV_ENABLE_OFFSET, bitmask, bitmask); + return OK; +} +#endif + +/**************************************************************************** + * Name: litex_gpio_irq_disable + * + * Description: + * Disable the interrupt for specified GPIO IRQ port. + * + * Input Parameters: + * irq - The IRQ number to disable. Must be a hardware interrupt + * number as defined in irq.h. + * + ****************************************************************************/ + +#ifdef CONFIG_LITEX_GPIO_IRQ +void litex_gpio_irq_disable(int irq) +{ + up_disable_irq(irq); +} +#endif diff --git a/arch/risc-v/src/litex/litex_gpio.h b/arch/risc-v/src/litex/litex_gpio.h new file mode 100644 index 00000000000..105edf6bab9 --- /dev/null +++ b/arch/risc-v/src/litex/litex_gpio.h @@ -0,0 +1,210 @@ +/**************************************************************************** + * arch/risc-v/src/litex/litex_gpio.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_RISCV_SRC_LITEX_LITEX_GPIO_H +#define __ARCH_RISCV_SRC_LITEX_LITEX_GPIO_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Encoded pin attributes used with gpio_pinattr_t */ +#define GPIO_PINMODE_INPUT 1 /* Pin is set to input mode. */ +#define GPIO_PINMODE_OUTPUT 2 /* Pin is set to output mode. */ + +/* Encoded pin attributes used with gpio_intertype_t */ +#define GPIO_ISR_NONE 1 /* Disables interrupts */ +#define GPIO_ISR_RISING_EDGE 2 /* Interrupt generated on rising edge. */ +#define GPIO_ISR_FALLING_EDGE 4 /* Interrupt generated on falling edge. */ +#define GPIO_ISR_BOTH_EDGES 8 /* Interrupt generated on both edges. */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +/* Describes attributes used to configure GPIO pins */ + +typedef uint32_t gpio_pinattr_t; + +/* Describes attributes used to enable / disable GPIO interrupts. */ + +typedef uint32_t gpio_intrtype_t; + +struct gpio_isr_config_s +{ + uint32_t port; /* The GPIO port number to configure */ + uint32_t pin; /* The pin on the port to configure */ + gpio_intrtype_t type; /* The type of interrupt to use */ + xcpt_t handler; /* The handler to call on interrupt event */ + void * userdata; /* User data passed to the handler */ +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: litex_gpio_config + * + * Description: + * Configure a GPIO pin based on encoded pin attributes. GPIO's currently + * only support a single attribute. + * + * Input Parameters: + * port - The GPIO port number to read from. + * pin - The pin, or bit offset inside the GPIO port. + * attr - The pin attribute to apply to the pin. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +int litex_gpio_config(uint32_t port, uint32_t pin, gpio_pinattr_t attr); + +/**************************************************************************** + * Name: litex_gpio_write + * + * Description: + * Write one or zero to the selected GPIO pin. + * + * Input Parameters: + * port - The GPIO port number to read from. + * pin - The pin, or bit offset inside the GPIO port. + * + ****************************************************************************/ + +void litex_gpio_write(uint32_t port, uint32_t pin, bool value); + +/**************************************************************************** + * Name: litex_gpio_read + * + * Description: + * Read one or zero from the selected GPIO pin. + * + * Input Parameters: + * port - The GPIO port number to read from. + * pin - The pin, or bit offset inside the GPIO port. + * + * Returned Value: + * True if the pin is logic high. False if the pin is logic low. + * + ****************************************************************************/ + +bool litex_gpio_read(uint32_t port, uint32_t pin); + +/**************************************************************************** + * Name: litex_gpio_irq_enable + * + * Description: + * Initialize logic to support a second level of interrupt decoding for + * GPIO pins on the GPIO port. + * + * Input Parameters: + * irq - The IRQ number to enable. Must be a hardware interrupt + * number as defined in irq.h. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_LITEX_GPIO_IRQ +int litex_gpio_irq_enable(int irq); +#else +# define litex_gpio_irq_enable(irq) +#endif + +/**************************************************************************** + * Name: litex_gpio_irq_configure + * + * Description: + * Enable the interrupt for specified GPIO IRQ. + * + * Input Parameters: + * config - The interrupt configuration. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned + * on failure. + ****************************************************************************/ + +#ifdef CONFIG_LITEX_GPIO_IRQ +int litex_gpio_irq_config(struct gpio_isr_config_s * config); +#else +# define litex_gpio_irq_config(config) +#endif + +/**************************************************************************** + * Name: litex_gpio_irq_disable + * + * Description: + * Disable the interrupt for specified GPIO IRQ port. + * + * Input Parameters: + * irq - The IRQ number to disable. Must be a hardware interrupt + * number as defined in irq.h. + * + ****************************************************************************/ + +#ifdef CONFIG_LITEX_GPIO_IRQ +void litex_irq_disable(int irq); +#else +# define litex_irq_disable(irq) +#endif + +#ifdef __cplusplus +} +#endif +#undef EXTERN + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_RISCV_SRC_LITEX_LITEX_GPIO_H */ \ No newline at end of file