bsps/m68k: Implement new IRQ system

Implements the required bsp directives for the 'new' style rtems
interrupt system. Previously, uC5282 only supported interrupts through
the BSP_installVME_isr directive.

BSP_installVME_isr still behaves the same, but is instead implemented
using the new IRQ directives. To ensure compatibility with existing
EPICS related code without causing subtle bugs, BSP_installVME_isr will
also poke at the EPORT interrupt settings to enable IRQ1 when installing
a "fake VME" interrupt (vector >= 192).

This also fixes a regression from e9cb088995 that broke clock interrupt
handlers, leading to the BSP crashing shortly after boot.
This commit is contained in:
Jeremy Lorelli
2025-12-28 02:18:54 -08:00
parent c90d57b12c
commit 0b5d311673
4 changed files with 502 additions and 207 deletions

View File

@@ -1 +1,40 @@
#include <bsp/irq-default.h>
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup RTEMSBSPsm68kuC5282
*
* @brief Core IRQ definitions
*/
/*
* Copyright (c) 2025 Jeremy Lorelli <lorelli@slac.stanford.edu>
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#ifndef LIBBSP_M68K_UC5282_IRQ_H
#define LIBBSP_M68K_UC5282_IRQ_H
#include <rtems.h>
#include <rtems/irq.h>
#include <rtems/irq-extension.h>
#define BSP_INTERRUPT_CUSTOM_VALID_VECTOR
#define BSP_INTERRUPT_VECTOR_COUNT 192
#define BSP_VME_INTERRUPT_VECTOR_COUNT (255 - BSP_INTERRUPT_VECTOR_COUNT)
#define BSP_FIRST_VME_INTERRUPT_VECTOR BSP_INTERRUPT_VECTOR_COUNT
extern void uC5282_interrupt_vector_install(rtems_vector_number vector);
extern void uC5282_interrupt_vector_remove(rtems_vector_number vector);
#define bsp_interrupt_vector_install(v) uC5282_interrupt_vector_install(v)
#define bsp_interrupt_vector_remove(v) uC5282_interrupt_vector_remove(v)
#endif // LIBBSP_M68K_UC5282_IRQ_H

456
bsps/m68k/uC5282/irq/irq.c Normal file
View File

@@ -0,0 +1,456 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2025 Jeremy Lorelli <lorelli@slac.stanford.edu>
* Copyright (c) 2005 Eric Norum <eric@norum.ca>
* COPYRIGHT (c) 2005.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <bsp.h>
#include <bsp/irq.h>
#include <mcf5282/mcf5282.h>
#include <bsp/irq-generic.h>
typedef void(*void_func_t)(void);
extern void bsp_default_isr_handler(int);
/*
* Adjust interrupt vector to an offset in INTCn register
* MCF5282 interrupt vector mapping:
* * 0-63 -- internal CF interrupts
* * 64-127 -- INTC0
* * 128-191 -- INTC1
* * 192-255 -- Unused by hardware; used for fake interrupts in software
*/
static int vector_to_offset(int vector)
{
if (vector < 128 && vector >= 64)
return vector - 64;
else if (vector < 192)
return vector - 128;
return 0;
}
/*
* Get the associated INTCn chip.
* 0 == INTC0, 1 == INTC1, -1 == neither
*/
static int vector_to_source(int vector)
{
if (vector < 128 && vector >= 64)
return 0;
else if (vector < 192 && vector >= 128)
return 1;
return -1;
}
bool bsp_interrupt_is_valid_vector(rtems_vector_number vector)
{
return vector < BSP_INTERRUPT_VECTOR_COUNT;
}
static uint32_t s_wasInstalled[6];
static bool bsp_interrupt_was_installed(int vector)
{
return !!(s_wasInstalled[vector/32] & (1<<vector%32));
}
static void bsp_interrupt_mark_installed(int vector)
{
s_wasInstalled[vector/32] |= (1<<vector%32);
}
/*
* Interrupt controller allocation. Mark a level and priority used,
* each interrupt must have a unique level/prio
*/
rtems_status_code bsp_allocate_interrupt(int level, int priority)
{
static char used[7];
rtems_interrupt_level l;
rtems_status_code ret = RTEMS_RESOURCE_IN_USE;
if ((level < 1) || (level > 7) || (priority < 0) || (priority > 7))
return RTEMS_INVALID_NUMBER;
rtems_interrupt_disable(l);
if ((used[level-1] & (1 << priority)) == 0) {
used[level-1] |= (1 << priority);
ret = RTEMS_SUCCESSFUL;
}
rtems_interrupt_enable(l);
return ret;
}
static void enable_irq(int which, int off)
{
if (off < 32) {
*(which ? &MCF5282_INTC1_IMRL : &MCF5282_INTC0_IMRL) &=
~(1<<off | MCF5282_INTC_IMRL_MASKALL);
}
else {
*(which ? &MCF5282_INTC1_IMRH : &MCF5282_INTC0_IMRH) &= ~(1<<(off-32));
}
}
rtems_status_code bsp_interrupt_vector_enable(rtems_vector_number vector)
{
int off;
int which;
int level;
int prio;
volatile uint8_t* reg;
if (!bsp_interrupt_is_valid_vector(vector))
return RTEMS_UNSATISFIED;
if ((which = vector_to_source(vector)) < 0) {
return RTEMS_SUCCESSFUL; /* nothing to do */
}
off = vector_to_offset(vector);
/* check if the interrupt has already been setup */
if (!bsp_interrupt_was_installed(vector)) {
bsp_interrupt_mark_installed(vector);
/* attempt to map a unique priority/level to the interrupt. Uniqueness is
* required, otherwise we get UB */
for (level = 1; level < 8; level++) {
for (prio = 0; prio < 8; prio++) {
if (bsp_allocate_interrupt(level, prio) == RTEMS_SUCCESSFUL) {
MCF5282_EPORT_EPIER;
/* ICR1-8 are fixed level/prio. the rest need setup */
if (off > 8) {
reg = (which ? &MCF5282_INTC1_ICR09 - 8 : &MCF5282_INTC0_ICR1);
reg += off - 1;
/* configure level and prio */
*reg |= MCF5282_INTC_ICR_IL(level) |
MCF5282_INTC_ICR_IP(prio);
}
enable_irq(which, off);
return RTEMS_SUCCESSFUL;
}
}
}
return RTEMS_UNSATISFIED;
}
enable_irq(which, off);
return RTEMS_SUCCESSFUL;
}
rtems_status_code bsp_interrupt_get_attributes(
rtems_vector_number vector,
rtems_interrupt_attributes *attributes
)
{
return RTEMS_SUCCESSFUL;
}
rtems_status_code bsp_interrupt_is_pending(
rtems_vector_number vector,
bool *pending
)
{
bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
bsp_interrupt_assert(pending != NULL);
*pending = false;
return RTEMS_UNSATISFIED;
}
rtems_status_code bsp_interrupt_raise(rtems_vector_number vector)
{
int off;
int which;
bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
if ((which = vector_to_source(vector)) < 0) {
return RTEMS_UNSATISFIED;
}
off = vector_to_offset(vector);
if (off < 32)
*(which ? &MCF5282_INTC1_INTFRCL : &MCF5282_INTC0_INTFRCL) |= (1<<off);
else
*(which ? &MCF5282_INTC1_INTFRCH : &MCF5282_INTC0_INTFRCH) |= (1<<(off-32));
return RTEMS_SUCCESSFUL;
}
rtems_status_code bsp_interrupt_clear(rtems_vector_number vector)
{
bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
return RTEMS_UNSATISFIED;
}
rtems_status_code bsp_interrupt_vector_is_enabled(
rtems_vector_number vector,
bool *enabled
)
{
bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
bsp_interrupt_assert(enabled != NULL);
*enabled = false;
return RTEMS_UNSATISFIED;
}
rtems_status_code bsp_interrupt_vector_disable(rtems_vector_number vector)
{
int off;
int which;
bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
if ((which = vector_to_source(vector)) < 0) {
return RTEMS_SUCCESSFUL; /* nothing to do */
}
off = vector_to_offset(vector);
/* mask the interrupt */
if (off < 32) {
*(which ? &MCF5282_INTC1_IMRL : &MCF5282_INTC0_IMRL) |= 1<<off;
}
else {
*(which ? &MCF5282_INTC1_IMRH : &MCF5282_INTC0_IMRH) |= 1<<(off-32);
}
return RTEMS_SUCCESSFUL;
}
rtems_status_code bsp_interrupt_set_priority(
rtems_vector_number vector,
uint32_t priority
)
{
bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
return RTEMS_UNSATISFIED;
}
rtems_status_code bsp_interrupt_get_priority(
rtems_vector_number vector,
uint32_t *priority
)
{
bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
bsp_interrupt_assert(priority != NULL);
return RTEMS_UNSATISFIED;
}
void bsp_interrupt_facility_initialize(void)
{
}
static void set_exception_handler(
rtems_vector_number vector,
void_func_t handler
)
{
void **vbr;
void_func_t *exception_table;
m68k_get_vbr(vbr);
exception_table = (void_func_t*)vbr;
exception_table[vector] = handler;
}
static void dispatch_handler(rtems_vector_number exception_vector)
{
bsp_interrupt_handler_dispatch_unchecked(exception_vector);
}
void uC5282_interrupt_vector_install(rtems_vector_number vector)
{
_ISR_Vector_table[vector] = dispatch_handler;
set_exception_handler(vector, _ISR_Handler);
}
void uC5282_interrupt_vector_remove(rtems_vector_number vector)
{
set_exception_handler(vector, (void_func_t)bsp_default_isr_handler);
}
/*
* 'VME' interrupt support
* Interrupt vectors 192-255 are set aside for use by external logic which
* drives IRQ1*. The actual interrupt source is read from the external
* logic at FPGA_IRQ_INFO. The most-significant bit of the least-significant
* byte read from this location is set as long as the external logic has
* interrupts to be serviced. The least-significant six bits indicate the
* interrupt source within the external logic and are used to select the
* specified interupt handler.
*/
#define FPGA_VECTOR (64+1) /* IRQ1* pin connected to external FPGA */
#define FPGA_IRQ_INFO *((vuint16 *)(0x31000000 + 0xfffffe))
#define NVECTORS (BSP_INTERRUPT_VECTOR_COUNT + BSP_VME_INTERRUPT_VECTOR_COUNT)
static struct handlerTab {
BSP_VME_ISR_t func;
void *arg;
} handlerTab[NVECTORS];
BSP_VME_ISR_t BSP_getVME_isr(unsigned long vector, void **pusrArg)
{
if (vector >= NVECTORS)
return (BSP_VME_ISR_t)NULL;
if (pusrArg)
*pusrArg = handlerTab[vector].arg;
return handlerTab[vector].func;
}
/*
* Trampoline for FPGA interrupts on the IRQ1* pin
*/
static void fpga_trampoline(void* arg)
{
rtems_vector_number v = (rtems_vector_number)arg;
/* ACK the interrupt */
MCF5282_EPORT_EPFR |= MCF5282_EPORT_EPFR_EPF1;
/*
* Handle FPGA interrupts until all have been consumed
*/
int loopcount = 0;
while (((v = FPGA_IRQ_INFO) & 0x80) != 0) {
v = 192 + (v & 0x3f);
if (++loopcount >= 50) {
rtems_interrupt_level level;
rtems_interrupt_disable(level);
printk(
"\nTOO MANY FPGA INTERRUPTS (LAST WAS 0x%x) -- "
"DISABLING ALL FPGA INTERRUPTS.\n",
v & 0x3f
);
MCF5282_INTC0_IMRL |= MCF5282_INTC_IMRL_INT1;
rtems_interrupt_enable(level);
return;
}
if (handlerTab[v].func) {
(*handlerTab[v].func)(handlerTab[v].arg, (unsigned long)v);
}
else {
rtems_interrupt_level level;
rtems_vector_number nv;
rtems_interrupt_disable(level);
printk("\nSPURIOUS FPGA INTERRUPT (0x%x).\n", v & 0x3f);
if ((((nv = FPGA_IRQ_INFO) & 0x80) != 0)
&& ((nv & 0x3f) == (v & 0x3f))) {
printk("DISABLING ALL FPGA INTERRUPTS.\n");
MCF5282_INTC0_IMRL |= MCF5282_INTC_IMRL_INT1;
}
rtems_interrupt_enable(level);
return;
}
}
}
/*
* Trampoline for legacy interrupts installed via BSP_installVME_isr
*/
static void trampoline(void* arg)
{
rtems_vector_number vec = (rtems_vector_number)arg;
if (handlerTab[vec].func)
(*handlerTab[vec].func)(handlerTab[vec].arg, (unsigned long)vec);
}
int BSP_installVME_isr(unsigned long vector, BSP_VME_ISR_t handler, void *usrArg)
{
rtems_interrupt_level level;
int i;
/*
* Register the handler information
*/
if (vector >= NVECTORS)
return -1;
handlerTab[vector].func = handler;
handlerTab[vector].arg = usrArg;
rtems_interrupt_disable(level);
/*
* If this is an external FPGA ('VME') vector set up the real IRQ.
*/
if ((vector >= BSP_FIRST_VME_INTERRUPT_VECTOR) && (vector <= 255)) {
static volatile int setupDone;
if (setupDone) {
rtems_interrupt_enable(level);
return 0;
}
setupDone = 1;
i = rtems_interrupt_handler_install(
FPGA_VECTOR,
"fpga_trampoline",
RTEMS_INTERRUPT_UNIQUE,
fpga_trampoline,
(void*)FPGA_VECTOR
);
if (i == RTEMS_SUCCESSFUL)
i = bsp_interrupt_vector_enable(FPGA_VECTOR);
/* enable IRQ1 EPORT interrupts as well */
MCF5282_EPORT_EPIER |= MCF5282_EPORT_EPIER_EPIE1;
rtems_interrupt_enable(level);
return i;
}
/*
* Make the connection between the interrupt and the local handler
*/
i = rtems_interrupt_handler_install(
vector,
"trampoline",
RTEMS_INTERRUPT_UNIQUE,
trampoline,
(void*)vector
);
if (i != RTEMS_SUCCESSFUL) {
rtems_interrupt_enable(level);
return i;
}
/* for compatibility with older code: Enable IRQn interrupts on EPORT too.
* This used to be done in init_intc0_bit */
if (vector <= (64 + 7) && vector > 64) {
MCF5282_EPORT_EPIER |= 1 << (vector - 64);
}
i = bsp_interrupt_vector_enable(vector);
rtems_interrupt_enable(level);
return i;
}
int BSP_removeVME_isr(unsigned long vector, BSP_VME_ISR_t handler, void *usrArg)
{
if (vector >= NVECTORS)
return -1;
if ((handlerTab[vector].func != handler)
|| (handlerTab[vector].arg != usrArg))
return -1;
handlerTab[vector].func = (BSP_VME_ISR_t)NULL;
return 0;
}

View File

@@ -17,6 +17,7 @@
#include <bsp.h>
#include <bsp/bootcard.h>
#include <bsp/irq-generic.h>
#include <rtems/error.h>
#include <errno.h>
#include <stdio.h>
@@ -322,6 +323,8 @@ void bsp_start( void )
"System clock speed: %" PRIu32 "Hz\n", bsp_get_CPU_clock_speed()
);
}
bsp_interrupt_initialize();
}
uint32_t bsp_get_CPU_clock_speed(void)
@@ -329,27 +332,6 @@ uint32_t bsp_get_CPU_clock_speed(void)
return( BSP_sys_clk_speed );
}
/*
* Interrupt controller allocation
*/
rtems_status_code
bsp_allocate_interrupt(int level, int priority)
{
static char used[7];
rtems_interrupt_level l;
rtems_status_code ret = RTEMS_RESOURCE_IN_USE;
if ((level < 1) || (level > 7) || (priority < 0) || (priority > 7))
return RTEMS_INVALID_NUMBER;
rtems_interrupt_disable(l);
if ((used[level-1] & (1 << priority)) == 0) {
used[level-1] |= (1 << priority);
ret = RTEMS_SUCCESSFUL;
}
rtems_interrupt_enable(l);
return ret;
}
/*
* Arcturus bootloader system calls
*/
@@ -482,190 +464,6 @@ int BSP_disableVME_int_lvl(unsigned int level)
return 0;
}
/*
* 'VME' interrupt support
* Interrupt vectors 192-255 are set aside for use by external logic which
* drives IRQ1*. The actual interrupt source is read from the external
* logic at FPGA_IRQ_INFO. The most-significant bit of the least-significant
* byte read from this location is set as long as the external logic has
* interrupts to be serviced. The least-significant six bits indicate the
* interrupt source within the external logic and are used to select the
* specified interupt handler.
*/
#define NVECTOR 256
#define FPGA_VECTOR (64+1) /* IRQ1* pin connected to external FPGA */
#define FPGA_IRQ_INFO *((vuint16 *)(0x31000000 + 0xfffffe))
static struct handlerTab {
BSP_VME_ISR_t func;
void *arg;
} handlerTab[NVECTOR];
BSP_VME_ISR_t
BSP_getVME_isr(unsigned long vector, void **pusrArg)
{
if (vector >= NVECTOR)
return (BSP_VME_ISR_t)NULL;
if (pusrArg)
*pusrArg = handlerTab[vector].arg;
return handlerTab[vector].func;
}
static rtems_isr
fpga_trampoline (rtems_vector_number v)
{
/*
* Handle FPGA interrupts until all have been consumed
*/
int loopcount = 0;
while (((v = FPGA_IRQ_INFO) & 0x80) != 0) {
v = 192 + (v & 0x3f);
if (++loopcount >= 50) {
rtems_interrupt_level level;
rtems_interrupt_disable(level);
printk(
"\nTOO MANY FPGA INTERRUPTS (LAST WAS 0x%x) -- "
"DISABLING ALL FPGA INTERRUPTS.\n",
v & 0x3f
);
MCF5282_INTC0_IMRL |= MCF5282_INTC_IMRL_INT1;
rtems_interrupt_enable(level);
return;
}
if (handlerTab[v].func) {
(*handlerTab[v].func)(handlerTab[v].arg, (unsigned long)v);
}
else {
rtems_interrupt_level level;
rtems_vector_number nv;
rtems_interrupt_disable(level);
printk("\nSPURIOUS FPGA INTERRUPT (0x%x).\n", v & 0x3f);
if ((((nv = FPGA_IRQ_INFO) & 0x80) != 0)
&& ((nv & 0x3f) == (v & 0x3f))) {
printk("DISABLING ALL FPGA INTERRUPTS.\n");
MCF5282_INTC0_IMRL |= MCF5282_INTC_IMRL_INT1;
}
rtems_interrupt_enable(level);
return;
}
}
}
static rtems_isr
trampoline (rtems_vector_number v)
{
if (handlerTab[v].func)
(*handlerTab[v].func)(handlerTab[v].arg, (unsigned long)v);
}
static void
enable_irq(unsigned source)
{
rtems_interrupt_level level;
rtems_interrupt_disable(level);
if (source >= 32)
MCF5282_INTC0_IMRH &= ~(1 << (source - 32));
else
MCF5282_INTC0_IMRL &= ~((1 << source) |
MCF5282_INTC_IMRL_MASKALL);
rtems_interrupt_enable(level);
}
static int
init_intc0_bit(unsigned long vector)
{
rtems_interrupt_level level;
/*
* Find an unused level/priority if this is an on-chip (INTC0)
* source and this is the first time the source is being used.
* Interrupt sources 1 through 7 are fixed level/priority
*/
if ((vector >= 65) && (vector <= 127)) {
int l, p;
int source = vector - 64;
static unsigned char installed[8];
rtems_interrupt_disable(level);
if (installed[source/8] & (1 << (source % 8))) {
rtems_interrupt_enable(level);
return 0;
}
installed[source/8] |= (1 << (source % 8));
rtems_interrupt_enable(level);
for (l = 1 ; l < 7 ; l++) {
for (p = 0 ; p < 8 ; p++) {
if ((source < 8)
|| (bsp_allocate_interrupt(l,p) == RTEMS_SUCCESSFUL)) {
if (source < 8)
MCF5282_EPORT_EPIER |= 1 << source;
else
*(&MCF5282_INTC0_ICR1 + (source - 1)) =
MCF5282_INTC_ICR_IL(l) |
MCF5282_INTC_ICR_IP(p);
enable_irq(source);
return 0;
}
}
}
return -1;
}
return 0;
}
int
BSP_installVME_isr(unsigned long vector, BSP_VME_ISR_t handler, void *usrArg)
{
rtems_isr_entry old_handler;
rtems_interrupt_level level;
/*
* Register the handler information
*/
if (vector >= NVECTOR)
return -1;
handlerTab[vector].func = handler;
handlerTab[vector].arg = usrArg;
/*
* If this is an external FPGA ('VME') vector set up the real IRQ.
*/
if ((vector >= 192) && (vector <= 255)) {
int i;
static volatile int setupDone;
rtems_interrupt_disable(level);
if (setupDone) {
rtems_interrupt_enable(level);
return 0;
}
setupDone = 1;
rtems_interrupt_catch(fpga_trampoline, FPGA_VECTOR, &old_handler);
i = init_intc0_bit(FPGA_VECTOR);
rtems_interrupt_enable(level);
return i;
}
/*
* Make the connection between the interrupt and the local handler
*/
rtems_interrupt_catch(trampoline, vector, &old_handler);
return init_intc0_bit(vector);
}
int
BSP_removeVME_isr(unsigned long vector, BSP_VME_ISR_t handler, void *usrArg)
{
if (vector >= NVECTOR)
return -1;
if ((handlerTab[vector].func != handler)
|| (handlerTab[vector].arg != usrArg))
return -1;
handlerTab[vector].func = (BSP_VME_ISR_t)NULL;
return 0;
}
int
BSP_vme2local_adrs(
unsigned am, unsigned long vmeaddr, unsigned long *plocaladdr)

View File

@@ -29,7 +29,7 @@ links:
- role: build-dependency
uid: ../../obj
- role: build-dependency
uid: ../../objirqdflt
uid: ../../objirq
- role: build-dependency
uid: ../../objmem
- role: build-dependency
@@ -46,6 +46,8 @@ source:
- bsps/m68k/uC5282/start/bspreset.c
- bsps/m68k/uC5282/start/bspstart.c
- bsps/m68k/uC5282/start/init5282.c
- bsps/m68k/uC5282/irq/irq.c
- bsps/shared/irq/irq-default-handler.c
- bsps/shared/dev/getentropy/getentropy-cpucounter.c
- bsps/shared/start/gettargethash-default.c
- bsps/shared/start/sbrk.c