1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-10-19 02:29:40 +00:00

Revamp XLR interrupt handling, the previous scheme does not work well on

SMP.

We used to route all PIC based interrupts to cpu 0, and used the per-CPU
interrupt mask to enable/disable interrupts. But the interrupt threads can
run on any cpu on SMP, and the interrupt thread will re-enable the interrupts
on the CPU it runs on when it is done, and not on cpu0 where the PIC will
still send interrupts to.

The fix is move the disable/enable for PIC based interrupts to PIC, we will
ack on PIC only when the interrupt thread is done, and we do not use the
per-CPU interrupt mask.

The changes also introduce a way for subsystems to add a function that
will be called to clear the interrupt on the subsystem. Currently This is
used by the PCI/PCIe for doing additional work during the interrupt
handling.
This commit is contained in:
Jayachandran C. 2010-08-27 19:53:57 +00:00
parent 98b9eb0db2
commit b47f51b4a0
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=211893
6 changed files with 114 additions and 133 deletions

View File

@ -40,10 +40,9 @@
* XLR needs custom pre and post handlers for PCI/PCI-e interrupts
* XXX: maybe follow i386 intsrc model
*/
void xlr_cpu_establish_hardintr(const char *, driver_filter_t *,
driver_intr_t *, void *, int, int, void **, void (*)(void *),
void (*)(void *), void (*)(void *), int (*)(void *, u_char));
void xlr_mask_hard_irq(void *);
void xlr_unmask_hard_irq(void *);
void xlr_establish_intr(const char *name, driver_filter_t filt,
driver_intr_t handler, void *arg, int irq, int flags,
void **cookiep, void (*busack)(int));
void xlr_enable_irq(int irq);
#endif /* _RMI_INTERRUPT_H_ */

View File

@ -49,34 +49,76 @@ __FBSDID("$FreeBSD$");
#include <mips/rmi/clock.h>
#include <mips/rmi/pic.h>
/*#include <machine/intrcnt.h>*/
struct xlr_intrsrc {
void (*busack)(int); /* Additional ack */
struct intr_event *ie; /* event corresponding to intr */
int irq;
};
static struct xlr_intrsrc xlr_interrupts[XLR_MAX_INTR];
static mips_intrcnt_t mips_intr_counters[XLR_MAX_INTR];
static struct intr_event *mips_intr_events[XLR_MAX_INTR];
static int intrcnt_index;
void
xlr_mask_hard_irq(void *source)
xlr_enable_irq(int irq)
{
uintptr_t irq = (uintptr_t) source;
write_c0_eimr64(read_c0_eimr64() & ~(1ULL << irq));
}
void
xlr_unmask_hard_irq(void *source)
{
uintptr_t irq = (uintptr_t) source;
write_c0_eimr64(read_c0_eimr64() | (1ULL << irq));
}
void
xlr_cpu_establish_hardintr(const char *name, driver_filter_t * filt,
void (*handler) (void *), void *arg, int irq, int flags, void **cookiep,
void (*pre_ithread)(void *), void (*post_ithread)(void *),
void (*post_filter)(void *), int (*assign_cpu)(void *, u_char))
cpu_establish_softintr(const char *name, driver_filter_t * filt,
void (*handler) (void *), void *arg, int irq, int flags,
void **cookiep)
{
panic("Soft interrupts unsupported!\n");
}
void
cpu_establish_hardintr(const char *name, driver_filter_t * filt,
void (*handler) (void *), void *arg, int irq, int flags,
void **cookiep)
{
xlr_establish_intr(name, filt, handler, arg, irq, flags,
cookiep, NULL);
}
static void
xlr_post_filter(void *source)
{
struct xlr_intrsrc *src = source;
if (src->busack)
src->busack(src->irq);
pic_ack(PIC_IRQ_TO_INTR(src->irq));
}
static void
xlr_pre_ithread(void *source)
{
struct xlr_intrsrc *src = source;
if (src->busack)
src->busack(src->irq);
}
static void
xlr_post_ithread(void *source)
{
struct xlr_intrsrc *src = source;
pic_ack(PIC_IRQ_TO_INTR(src->irq));
}
void
xlr_establish_intr(const char *name, driver_filter_t filt,
driver_intr_t handler, void *arg, int irq, int flags,
void **cookiep, void (*busack)(int))
{
struct intr_event *ie; /* descriptor for the IRQ */
struct xlr_intrsrc *src = NULL;
int errcode;
if (irq < 0 || irq > XLR_MAX_INTR)
@ -86,43 +128,37 @@ xlr_cpu_establish_hardintr(const char *name, driver_filter_t * filt,
* FIXME locking - not needed now, because we do this only on
* startup from CPU0
*/
ie = mips_intr_events[irq];
/* mih->cntp = &intrcnt[irq]; */
if (ie == NULL) {
errcode = intr_event_create(&ie, (void *)(uintptr_t) irq, 0,
irq, pre_ithread, post_ithread, post_filter, assign_cpu,
"hard intr%d:", irq);
printf("[%s] Setup intr %d called on cpu %d (%d)\n", name, irq,
xlr_cpu_id(), PCPU_GET(cpuid));
src = &xlr_interrupts[irq];
ie = src->ie;
if (ie == NULL) {
/*
* PIC based interrupts need ack in PIC, and some SoC
* components need additional acks (e.g. PCI)
*/
if (PIC_IRQ_IS_PICINTR(irq))
errcode = intr_event_create(&ie, src, 0, irq,
xlr_pre_ithread, xlr_post_ithread, xlr_post_filter,
NULL, "hard intr%d:", irq);
else {
if (filt == NULL)
panic("Not supported - non filter percpu intr");
errcode = intr_event_create(&ie, src, 0, irq,
NULL, NULL, NULL, NULL, "hard intr%d:", irq);
}
if (errcode) {
printf("Could not create event for intr %d\n", irq);
return;
}
mips_intr_events[irq] = ie;
src->irq = irq;
src->busack = busack;
src->ie = ie;
}
intr_event_add_handler(ie, name, filt, handler, arg,
intr_priority(flags), flags, cookiep);
xlr_unmask_hard_irq((void *)(uintptr_t) irq);
}
void
cpu_establish_hardintr(const char *name, driver_filter_t * filt,
void (*handler) (void *), void *arg, int irq, int flags, void **cookiep)
{
xlr_cpu_establish_hardintr(name, filt, handler, arg, irq,
flags, cookiep, xlr_mask_hard_irq, xlr_unmask_hard_irq,
NULL, NULL);
}
void
cpu_establish_softintr(const char *name, driver_filter_t * filt,
void (*handler) (void *), void *arg, int irq, int flags,
void **cookiep)
{
/* we don't separate them into soft/hard like other mips */
xlr_cpu_establish_hardintr(name, filt, handler, arg, irq,
flags, cookiep, xlr_mask_hard_irq, xlr_unmask_hard_irq,
NULL, NULL);
xlr_enable_irq(irq);
}
void
@ -148,7 +184,7 @@ cpu_intr(struct trapframe *tf)
* compare which ACKs the interrupt.
*/
if (eirr & (1 << IRQ_TIMER)) {
intr_event_handle(mips_intr_events[IRQ_TIMER], tf);
intr_event_handle(xlr_interrupts[IRQ_TIMER].ie, tf);
critical_exit();
return;
}
@ -158,7 +194,7 @@ cpu_intr(struct trapframe *tf)
if ((eirr & (1ULL << i)) == 0)
continue;
ie = mips_intr_events[i];
ie = xlr_interrupts[i].ie;
/* Don't account special IRQs */
switch (i) {
case IRQ_IPI:
@ -167,16 +203,12 @@ cpu_intr(struct trapframe *tf)
default:
mips_intrcnt_inc(mips_intr_counters[i]);
}
/* Ack the IRQ on the CPU */
write_c0_eirr64(1ULL << i);
pic_ack(i);
if (!ie || TAILQ_EMPTY(&ie->ie_handlers)) {
printf("stray interrupt %d\n", i);
continue;
}
if (intr_event_handle(ie, tf) != 0) {
printf("stray interrupt %d\n", i);
}
pic_delayed_ack(i);
}
critical_exit();
}

View File

@ -99,7 +99,7 @@ iodi_setup_intr(device_t dev, device_t child,
/* FIXME uart 1? */
cpu_establish_hardintr("uart", filt, intr, arg,
PIC_UART_0_IRQ, flags, cookiep);
pic_setup_intr(PIC_IRT_UART_0_INDEX, PIC_UART_0_IRQ, 0x1);
pic_setup_intr(PIC_IRT_UART_0_INDEX, PIC_UART_0_IRQ, 0x1, 0);
} else if (strcmp(device_get_name(child), "rge") == 0) {
int irq;
@ -107,11 +107,11 @@ iodi_setup_intr(device_t dev, device_t child,
irq = (intptr_t)ires->__r_i;
cpu_establish_hardintr("rge", filt, intr, arg, irq, flags,
cookiep);
pic_setup_intr(irq - PIC_IRQ_BASE, irq, 0x1);
pic_setup_intr(irq - PIC_IRQ_BASE, irq, 0x1, 0);
} else if (strcmp(device_get_name(child), "ehci") == 0) {
cpu_establish_hardintr("ehci", filt, intr, arg, PIC_USB_IRQ, flags,
cookiep);
pic_setup_intr(PIC_USB_IRQ - PIC_IRQ_BASE, PIC_USB_IRQ, 0x1);
pic_setup_intr(PIC_USB_IRQ - PIC_IRQ_BASE, PIC_USB_IRQ, 0x1, 0);
}
return (0);

View File

@ -29,16 +29,15 @@
*
* RMI_BSD */
#ifndef _RMI_PIC_H_
#define _RMI_PIC_H_
#include <sys/cdefs.h>
#define _RMI_PIC_H_
#include <sys/cdefs.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <mips/rmi/iomap.h>
#define PIC_IRT_WD_INDEX 0
#define PIC_IRT_TIMER_INDEX(i) (1 + (i))
#define PIC_IRT_CLOCK_INDEX PIC_IRT_TIMER_7_INDEX
#define PIC_IRT_UART_0_INDEX 9
#define PIC_IRT_UART_1_INDEX 10
#define PIC_IRT_I2C_0_INDEX 11
@ -70,7 +69,6 @@
#define PIC_IRT_PCIE_FATAL_INDEX 29
#define PIC_IRT_GPIO_B_INDEX 30
#define PIC_IRT_USB_INDEX 31
#define PIC_NUM_IRTS 32
#define PIC_CLOCK_TIMER 7
@ -102,7 +100,6 @@
#define PIC_TIMER_COUNT_1(i) (PIC_TIMER_COUNT_0_BASE + (i))
#define PIC_TIMER_HZ 66000000U
/*
* We use a simple mapping form PIC interrupts to CPU IRQs.
* The PIC interrupts 0-31 are mapped to CPU irq's 8-39.
@ -111,7 +108,7 @@
*/
#define PIC_IRQ_BASE 8
#define PIC_INTR_TO_IRQ(i) (PIC_IRQ_BASE + (i))
#define PIC_IRT_FIRST_IRQ PIC_IRQ_BASE
#define PIC_IRQ_TO_INTR(i) ((i) - PIC_IRQ_BASE)
#define PIC_WD_IRQ (PIC_IRQ_BASE + PIC_IRT_WD_INDEX)
#define PIC_TIMER_IRQ(i) (PIC_IRQ_BASE + PIC_IRT_TIMER_INDEX(i))
@ -137,7 +134,6 @@
#define PIC_BRIDGE_BERR_IRQ (PIC_IRQ_BASE + PIC_IRT_BRIDGE_BERR_INDEX)
#define PIC_BRIDGE_TB_IRQ (PIC_IRQ_BASE + PIC_IRT_BRIDGE_TB_INDEX)
#define PIC_BRIDGE_AERR_NMI_IRQ (PIC_IRQ_BASE + PIC_IRT_BRIDGE_AERR_NMI_INDEX)
#define PIC_BRIDGE_ERR_IRQ (PIC_IRQ_BASE + PIC_IRT_BRIDGE_ERR_INDEX)
#define PIC_PCIE_LINK0_IRQ (PIC_IRQ_BASE + PIC_IRT_PCIE_LINK0_INDEX)
#define PIC_PCIE_LINK1_IRQ (PIC_IRQ_BASE + PIC_IRT_PCIE_LINK1_INDEX)
@ -148,9 +144,10 @@
#define PIC_GPIO_B_IRQ (PIC_IRQ_BASE + PIC_IRT_GPIO_B_INDEX)
#define PIC_USB_IRQ (PIC_IRQ_BASE + PIC_IRT_USB_INDEX)
#define PIC_IRT_LAST_IRQ PIC_USB_IRQ
#define PIC_IRQ_IS_EDGE_TRIGGERED(irq) (((irq) >= PIC_TIMER_IRQ(0)) && ((irq) <= PIC_TIMER_IRQ(7)))
#define PIC_IRQ_IS_IRT(irq) (((irq) >= PIC_IRT_FIRST_IRQ) && ((irq) <= PIC_IRT_LAST_IRQ))
#define PIC_IRQ_IS_PICINTR(irq) ((irq) >= PIC_IRQ_BASE && \
(irq) < PIC_IRQ_BASE + PIC_NUM_IRTS)
#define PIC_IS_EDGE_TRIGGERED(i) ((i) >= PIC_IRT_TIMER_INDEX(0) && \
(i) <= PIC_IRT_TIMER_INDEX(7))
extern struct mtx xlr_pic_lock;
@ -187,35 +184,11 @@ pic_update_control(__uint32_t control)
}
static __inline void
pic_ack(int irq)
pic_ack(int picintr)
{
xlr_reg_t *mmio = xlr_io_mmio(XLR_IO_PIC_OFFSET);
/* ack the pic, if needed */
if (!PIC_IRQ_IS_IRT(irq))
return;
if (PIC_IRQ_IS_EDGE_TRIGGERED(irq)) {
mtx_lock_spin(&xlr_pic_lock);
xlr_write_reg(mmio, PIC_INT_ACK, (1 << (irq - PIC_IRQ_BASE)));
mtx_unlock_spin(&xlr_pic_lock);
}
return;
}
static __inline void
pic_delayed_ack(int irq)
{
xlr_reg_t *mmio = xlr_io_mmio(XLR_IO_PIC_OFFSET);
if (!PIC_IRQ_IS_IRT(irq))
return;
if (!PIC_IRQ_IS_EDGE_TRIGGERED(irq)) {
mtx_lock_spin(&xlr_pic_lock);
xlr_write_reg(mmio, PIC_INT_ACK, (1 << (irq - PIC_IRQ_BASE)));
mtx_unlock_spin(&xlr_pic_lock);
}
return;
xlr_write_reg(mmio, PIC_INT_ACK, 1 << picintr);
}
static __inline
@ -230,13 +203,11 @@ void pic_send_ipi(int cpu, int ipi)
}
static __inline
void pic_setup_intr(int picintr, int irq, uint32_t cpumask)
void pic_setup_intr(int picintr, int irq, uint32_t cpumask, int level)
{
xlr_reg_t *mmio = xlr_io_mmio(XLR_IO_PIC_OFFSET);
int level;
mtx_lock_spin(&xlr_pic_lock);
level = PIC_IRQ_IS_EDGE_TRIGGERED(irq);
xlr_write_reg(mmio, PIC_IRT_0(picintr), cpumask);
xlr_write_reg(mmio, PIC_IRT_1(picintr), ((1 << 31) | (level << 30) |
(1 << 6) | irq));

View File

@ -305,7 +305,7 @@ xlr_pic_init(void)
/* Initialize all IRT entries */
for (i = 0; i < PIC_NUM_IRTS; i++) {
irq = PIC_INTR_TO_IRQ(i);
level = PIC_IRQ_IS_EDGE_TRIGGERED(irq);
level = PIC_IS_EDGE_TRIGGERED(i);
/* Bind all PIC irqs to cpu 0 */
xlr_write_reg(mmio, PIC_IRT_0(i), 0x01);
@ -575,11 +575,11 @@ platform_init_ap(int cpuid)
stat |= MIPS_SR_COP_2_BIT | MIPS_SR_COP_0_BIT;
mips_wr_status(stat);
xlr_unmask_hard_irq((void *)IRQ_IPI);
xlr_unmask_hard_irq((void *)IRQ_TIMER);
xlr_enable_irq(IRQ_IPI);
xlr_enable_irq(IRQ_TIMER);
if (xlr_thr_id() == 0) {
xlr_msgring_cpu_init();
xlr_unmask_hard_irq((void *)IRQ_MSGRING);
xlr_enable_irq(IRQ_MSGRING);
}
return;

View File

@ -403,24 +403,15 @@ xlr_map_msi(device_t pcib, device_t dev, int irq, uint64_t * addr,
}
static void
bridge_pcix_ack(void *arg)
bridge_pcix_ack(int irq)
{
xlr_read_reg(xlr_io_mmio(XLR_IO_PCIX_OFFSET), 0x140 >> 2);
}
static void
bridge_pcix_mask_ack(void *arg)
bridge_pcie_ack(int irq)
{
xlr_mask_hard_irq(arg);
bridge_pcix_ack(arg);
}
static void
bridge_pcie_ack(void *arg)
{
int irq = (intptr_t)arg;
uint32_t reg;
xlr_reg_t *pcie_mmio_le = xlr_io_mmio(XLR_IO_PCIE_1_OFFSET);
@ -443,14 +434,6 @@ bridge_pcie_ack(void *arg)
xlr_write_reg(pcie_mmio_le, reg>>2, 0xffffffff);
}
static void
bridge_pcie_mask_ack(void *arg)
{
xlr_mask_hard_irq(arg);
bridge_pcie_ack(arg);
}
static int
mips_platform_pci_setup_intr(device_t dev, device_t child,
struct resource *irq, int flags,
@ -475,17 +458,13 @@ mips_platform_pci_setup_intr(device_t dev, device_t child,
return (0);
if (xlr_board_info.is_xls == 0) {
xlr_cpu_establish_hardintr(device_get_name(child), filt,
intr, arg, PIC_PCIX_IRQ, flags, cookiep,
bridge_pcix_mask_ack, xlr_unmask_hard_irq,
bridge_pcix_ack, NULL);
pic_setup_intr(PIC_IRT_PCIX_INDEX, PIC_PCIX_IRQ, 0x1);
xlr_establish_intr(device_get_name(child), filt,
intr, arg, PIC_PCIX_IRQ, flags, cookiep, bridge_pcix_ack);
pic_setup_intr(PIC_IRT_PCIX_INDEX, PIC_PCIX_IRQ, 0x1, 0);
} else {
xlr_cpu_establish_hardintr(device_get_name(child), filt,
intr, arg, xlrirq, flags, cookiep,
bridge_pcie_mask_ack, xlr_unmask_hard_irq,
bridge_pcie_ack, NULL);
pic_setup_intr(xlrirq - PIC_IRQ_BASE, xlrirq, 0x1);
xlr_establish_intr(device_get_name(child), filt,
intr, arg, xlrirq, flags, cookiep, bridge_pcie_ack);
pic_setup_intr(xlrirq - PIC_IRQ_BASE, xlrirq, 0x1, 0);
}
return (bus_generic_setup_intr(dev, child, irq, flags, filt, intr,