arch/risc-v/src/mpfs: Add mpfs_gpiosetevent and gpio irq handling functions

Add a function to easily enable event handling on fabric and mss gpios. This
is similar to what exists e.g. for stm32 arm chips.

Also fix some small bugs in mpfs_configgpio related to IRQ bits configuration

Signed-off-by: Jukka Laitinen <jukkax@ssrc.tii.ae>
This commit is contained in:
Jukka Laitinen
2022-06-23 10:53:17 +04:00
committed by Xiang Xiao
parent 412539e66c
commit ba1b8d0712
2 changed files with 309 additions and 13 deletions
+279 -13
View File
@@ -27,18 +27,137 @@
#include "riscv_internal.h"
#include "hardware/mpfs_gpio.h"
#include "mpfs_gpio.h"
#include <arch/barriers.h>
/****************************************************************************
* Public Data
* Private Types
****************************************************************************/
static uintptr_t g_gpio_base[] =
struct gpio_callback_s
{
xcpt_t callback;
void *arg;
gpio_pinset_t cfgset;
};
/****************************************************************************
* Private Data
****************************************************************************/
static const uintptr_t g_gpio_base[] =
{
MPFS_GPIO0_LO_BASE, /* Bank-0 Normal GPIO */
MPFS_GPIO1_LO_BASE, /* Bank-1 Normal GPIO */
MPFS_GPIO2_LO_BASE /* Bank-2 Fabric only */
};
/* The GPIO interrupts are multiplexed. Total GPIO interrupts are 41.
* 41 = (14 from GPIO0 + 24 from GPIO1 + 3 non direct interrupts)
* GPIO2 interrupts are not available by default.
* Setting the corresponding bitcin GPIO_INTERRUPT_FAB_CR(31:0) will enable
* GPIO2(31:0) corresponding interrupt on PLIC.
*
* PLIC | GPIO_INTERRUPT_FAB_CR
* | 0 1
* -----------------------------------
* 0 | GPIO0 bit 0 GPIO2 bit 0
* 1 | GPIO0 bit 1 GPIO2 bit 1
* . |
* . |
* 12 | GPIO0 bit 12 GPIO2 bit 12
* 13 | GPIO0 bit 13 GPIO2 bit 13
* 14 | GPIO1 bit 0 GPIO2 bit 14
* 15 | GPIO1 bit 1 GPIO2 bit 15
* . |
* . |
* . |
* 30 | GPIO1 bit 16 GPIO2 bit 30
* 31 | GPIO1 bit 17 GPIO2 bit 31
* 32 | GPIO1 bit 18
* 33 | GPIO1 bit 19
* 34 | GPIO1 bit 20
* 35 | GPIO1 bit 21
* 36 | GPIO1 bit 22
* 37 | GPIO1 bit 23
* 38 Or of all GPIO0 interrupts who do not have a direct connection enabled
* 39 Or of all GPIO1 interrupts who do not have a direct connection enabled
* 40 Or of all GPIO2 interrupts who do not have a direct connection enabled
*/
/* IRQ handlers for GPIO2 (FABRIC) */
static struct gpio_callback_s g_fab_gpio_callbacks[GPIO_BANK2_NUM_PINS];
/* IRQ handlers for GPIO0 and GPIO1 (MSSIO) */
static struct gpio_callback_s g_mss_gpio_callbacks[GPIO_BANK0_NUM_PINS +
GPIO_BANK1_NUM_PINS];
/****************************************************************************
* Private Functions
****************************************************************************/
static void mpfs_gpio_irq_clear(int bank, int pin)
{
putreg32(1 << pin, g_gpio_base[bank] + MPFS_GPIO_INTR_OFFSET);
__DMB();
}
/****************************************************************************
* Interrupt Service Routines - Dispatchers
****************************************************************************/
static int mpfs_gpio_isr(int irq, void *context, void *arg)
{
uint8_t irq_n = irq - MPFS_IRQ_GPIO02_BIT0;
int ret = OK;
uint8_t mss_bank = irq_n < GPIO_BANK1_NUM_PINS ? 0 : 1;
uint8_t pin = mss_bank == 1 ? irq_n - GPIO_BANK0_NUM_PINS : irq_n;
uint32_t event_reg;
struct gpio_callback_s *cb;
/* if mss gpio interrupt is enabled and there is an event */
/* bank 0 and bank 1 pins */
cb = &g_mss_gpio_callbacks[irq_n];
event_reg = getreg32(g_gpio_base[mss_bank] + MPFS_GPIO_INTR_OFFSET);
/* if the callback is registered and there is an interrupt on the mss_pin */
if (cb->callback != NULL && (event_reg & (1 << pin)) != 0)
{
/* clear the pending interrupt */
mpfs_gpio_irq_clear(mss_bank, pin);
/* dispatch the interrupt to the handler */
ret = cb->callback(irq, context, cb->arg);
}
/* handle the muxed gpio bank2 interrupt */
if (irq_n < GPIO_BANK2_NUM_PINS)
{
cb = &g_fab_gpio_callbacks[irq_n];
event_reg = getreg32(g_gpio_base[2] + MPFS_GPIO_INTR_OFFSET);
if (cb->callback != NULL && (event_reg & (1 << pin)) != 0)
{
/* clear the pending interrupt */
mpfs_gpio_irq_clear(2, pin);
/* dispatch the interrupt to the handler */
ret = cb->callback(irq, context, cb->arg);
}
}
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
@@ -112,19 +231,24 @@ int mpfs_configgpio(gpio_pinset_t cfgset)
cfg |= GPIO_CONFIG_EN_OE_BUF;
}
if (cfgset & GPIO_IRQ_ENABLE)
{
cfg |= GPIO_CONFIG_EN_INT;
/* Clear irq bit */
putreg32(1 << pin, baseaddr + MPFS_GPIO_INTR_OFFSET);
}
/* set irq mode bits */
irq_mode &= 7;
cfg |= irq_mode << 5;
irq_mode &= GPIO_CONFIG_INT_MASK;
cfg |= irq_mode << GPIO_CONFIG_INT_SHIFT;
/* set/clear irq enable */
if (cfgset & GPIO_IRQ_ENABLE)
{
/* clear irq before enabling */
mpfs_gpio_irq_clear(bank, pin);
cfg |= GPIO_CONFIG_EN_INT;
}
else
{
cfg &= ~GPIO_CONFIG_EN_INT;
}
putreg32(cfg, baseaddr);
@@ -202,3 +326,145 @@ bool mpfs_gpioread(gpio_pinset_t pinset)
return ((getreg32(baseaddr + MPFS_GPIO_GPIN_OFFSET) & (1 << pin)) ? 1 : 0);
}
/****************************************************************************
* Name: mpfs_gpiosetevent
*
* Description:
* Sets/clears GPIO based event and interrupt triggers.
*
****************************************************************************/
int mpfs_gpiosetevent(gpio_pinset_t pinset, bool risingedge,
bool fallingedge, bool high, bool low, bool event,
xcpt_t func, void *arg)
{
uint8_t pin = (pinset & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT;
uint8_t bank = (pinset & GPIO_BANK_MASK) >> GPIO_BANK_SHIFT;
bool edge = risingedge || fallingedge;
bool level = high || low;
uint16_t irq_n = bank == 1 ? pin + GPIO_BANK0_NUM_PINS : pin;
uint16_t plic_irq;
struct gpio_callback_s *cb;
int ret;
if (bank > 2 || /* invalid bank */
(edge && level) || /* edge and level irq at the same */
(high && low) || /* level high and low at the same */
irq_n >= GPIO_BANK0_NUM_PINS + GPIO_BANK1_NUM_PINS ||
(bank == 2 && irq_n >= GPIO_BANK2_NUM_PINS))
{
return -EINVAL;
}
/* if func is NULL, make sure to disable interrupt */
if (func == NULL)
{
risingedge = false;
fallingedge = false;
high = false;
low = false;
}
cb = bank == 2 ? &g_fab_gpio_callbacks[irq_n]
: &g_mss_gpio_callbacks[irq_n];
/* make sure the GPIO is input */
pinset &= ~GPIO_MODE_MASK;
pinset |= GPIO_INPUT;
/* set the irq mask for the gpio */
pinset &= ~GPIO_IRQ_MASK;
if (edge)
{
if (risingedge && fallingedge)
{
pinset |= GPIO_IRQ_EDGE_BOTH;
}
else if (fallingedge)
{
pinset |= GPIO_IRQ_EDGE_NEG;
}
else if (risingedge)
{
pinset |= GPIO_IRQ_EDGE_POS;
}
}
else
{
if (high)
{
pinset |= GPIO_IRQ_HIGH;
}
else if (low)
{
pinset |= GPIO_IRQ_LOW;
}
}
/* if event, set ENABLE bit, otherwise leave it 0 */
if (event)
{
pinset |= GPIO_IRQ_ENABLE;
}
if (mpfs_configgpio(pinset) != OK)
{
return -EINVAL;
}
/* set/reset the GPIO_INTERRUPT_FAB_CR for bank 2 */
if (bank == 2)
{
if (event)
{
modifyreg32(MPFS_SYSREG_BASE +
MPFS_SYSREG_GPIO_INTERRUPT_FAB_CR_OFFSET,
0, SYSREG_GPIO_INTERRUPT_FAB_CR(pin));
}
else
{
modifyreg32(MPFS_SYSREG_BASE +
MPFS_SYSREG_GPIO_INTERRUPT_FAB_CR_OFFSET,
SYSREG_GPIO_INTERRUPT_FAB_CR(pin), 0);
}
}
/* calculate the plic vector location from gpio irq number */
plic_irq = irq_n + MPFS_IRQ_GPIO02_BIT0;
/* attach the plic irq */
if (event)
{
ret = irq_attach(plic_irq, mpfs_gpio_isr, NULL);
if (ret == OK)
{
cb->callback = func;
cb->arg = arg;
up_enable_irq(plic_irq);
}
}
else
{
ret = irq_detach(plic_irq);
if (ret == OK)
{
cb->callback = NULL;
}
/* disable the irq even in case detach fails */
up_disable_irq(plic_irq);
}
return ret;
}
+30
View File
@@ -138,6 +138,10 @@
#define GPIO_BANK1 (1 << GPIO_BANK_SHIFT)
#define GPIO_BANK2 (2 << GPIO_BANK_SHIFT)
#define GPIO_BANK0_NUM_PINS 14
#define GPIO_BANK1_NUM_PINS 24
#define GPIO_BANK2_NUM_PINS 32
/* This identifies the bit in the bank:
*
* 1111 1100 0000 0000
@@ -256,6 +260,32 @@ void mpfs_gpiowrite(gpio_pinset_t pinset, bool value);
bool mpfs_gpioread(gpio_pinset_t pinset);
/****************************************************************************
* Name: mpfs_gpiosetevent
*
* Description:
* Sets/clears GPIO based event and interrupt triggers.
*
* Input Parameters:
* - pinset: GPIO pin configuration
* - risingedge: Enables interrupt on rising edges
* - fallingedge: Enables interrupt on falling edges
* - high: Enables interrupt on level high
* - low: Enables interrupt on level low
* - event: Generate event when set
* - func: When non-NULL, generate interrupt
* - arg: Argument passed to the interrupt callback
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure indicating the
* nature of the failure.
*
****************************************************************************/
int mpfs_gpiosetevent(gpio_pinset_t pinset, bool risingedge,
bool fallingedge, bool high, bool low, bool event,
xcpt_t func, void *arg);
/****************************************************************************
* Name: mpfs_gpio_initialize
*