/* $NetBSD: interrupt.c,v 1.12 2023/10/06 11:45:16 skrll Exp $ */
/*-
* Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University.
* All rights reserved.
*
* Authors: Keith Bostic, Chris G. Demetriou
*
* Permission to use, copy, modify and distribute this software and
* its documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
* FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or
[email protected]
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*/
/*-
* Additional Copyright (c) 1997 by Matthew Jacob for NASA/Ames Research Center.
* Redistribute and modify at will, leaving only this additional copyright
* notice.
*/
#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */
__KERNEL_RCSID(0, "$NetBSD: interrupt.c,v 1.12 2023/10/06 11:45:16 skrll Exp $");
#include "opt_ddb.h"
#include <sys/param.h>
#include <sys/evcnt.h>
#include <sys/lwp.h>
#include <sys/proc.h>
#include <sys/kmem.h>
#include <sys/sched.h>
#include <machine/clock.h>
#include <machine/cpu.h>
#include <machine/cpufunc.h>
#include <machine/fpu.h>
#include <machine/frame.h>
#include <machine/intr.h>
#include <machine/md_var.h>
#include <machine/sapicvar.h>
#include <machine/smp.h>
#include <machine/userret.h>
#ifdef DDB
#include <ddb/ddb.h>
#endif
static void ia64_intr_eoi(void *);
static void ia64_intr_mask(void *);
#if 0
static void ia64_intr_unmask(void *);
#endif
static int ia64_dispatch_intr(void *, u_int);
#ifdef DDB
void db_print_vector(u_int, int);
#endif
int
interrupt(uint64_t vector, struct trapframe *tf)
{
struct cpu_info *ci = curcpu();
volatile struct ia64_interrupt_block *ib = IA64_INTERRUPT_BLOCK;
uint64_t adj, clk, itc;
int64_t delta;
uint8_t inta;
int count, handled = 0;
ia64_set_fpsr(IA64_FPSR_DEFAULT);
ci->ci_intrdepth++;
ci->ci_data.cpu_nintr++;
next:
/*
* Handle ExtINT interrupts by generating an INTA cycle to
* read the vector.
*/
if (vector == 0) {
inta = ib->ib_inta;
printf("ExtINT interrupt: vector=%u\n", (int)inta);
if (inta == 15) {
__asm __volatile("mov cr.eoi = r0;; srlz.d");
goto stray;
}
vector = (int)inta;
} else if (vector == 15)
goto stray;
if (vector == CLOCK_VECTOR) {/* clock interrupt */
itc = ia64_get_itc();
adj = ci->ci_clockadj;
clk = ci->ci_clock;
delta = itc - clk;
count = 0;
while (delta >= ia64_clock_reload) {
/* Only the BSP runs the real clock */
if (ci->ci_cpuid == 0)
hardclock((struct clockframe *)tf);
else
panic("CLOCK_VECTOR occur on not cpu0");
delta -= ia64_clock_reload;
clk += ia64_clock_reload;
count++;
handled = 1;
}
ia64_set_itm(ia64_get_itc() + ia64_clock_reload - adj);
if (count > 0) {
if (delta > (ia64_clock_reload >> 3)) {
adj = ia64_clock_reload >> 4;
} else
adj = 0;
} else
adj = 0;
ci->ci_clock = clk;
ci->ci_clockadj = adj;
ia64_srlz_d();
#ifdef MULTIPROCESSOR
} else if (vector == ipi_vector[IPI_AST]) {
asts[PCPU_GET(cpuid)]++;
CTR1(KTR_SMP, "IPI_AST, cpuid=%d", PCPU_GET(cpuid));
} else if (vector == ipi_vector[IPI_HIGH_FP]) {
struct thread *thr = PCPU_GET(fpcurthread);
if (thr != NULL) {
mtx_lock_spin(&thr->td_md.md_highfp_mtx);
save_high_fp(&thr->td_pcb->pcb_high_fp);
thr->td_pcb->pcb_fpcpu = NULL;
PCPU_SET(fpcurthread, NULL);
mtx_unlock_spin(&thr->td_md.md_highfp_mtx);
}
} else if (vector == ipi_vector[IPI_RENDEZVOUS]) {
rdvs[PCPU_GET(cpuid)]++;
CTR1(KTR_SMP, "IPI_RENDEZVOUS, cpuid=%d", PCPU_GET(cpuid));
enable_intr();
smp_rendezvous_action();
disable_intr();
} else if (vector == ipi_vector[IPI_STOP]) {
cpumask_t mybit = PCPU_GET(cpumask);
savectx(PCPU_PTR(pcb));
atomic_set_int(&stopped_cpus, mybit);
while ((started_cpus & mybit) == 0)
cpu_spinwait();
atomic_clear_int(&started_cpus, mybit);
atomic_clear_int(&stopped_cpus, mybit);
} else if (vector == ipi_vector[IPI_PREEMPT]) {
CTR1(KTR_SMP, "IPI_PREEMPT, cpuid=%d", PCPU_GET(cpuid));
__asm __volatile("mov cr.eoi = r0;; srlz.d");
enable_intr();
sched_preempt(curthread);
disable_intr();
goto stray;
#endif
} else {
ci->ci_intrdepth++;
handled = ia64_dispatch_intr(tf, vector);
ci->ci_intrdepth--;
}
__asm __volatile("mov cr.eoi = r0;; srlz.d");
vector = ia64_get_ivr();
if (vector != 15)
goto next;
stray:
if (TRAPF_USERMODE(tf)) {
enable_intr();
userret(curlwp);
do_ast(tf);
}
ci->ci_intrdepth--;
return handled;
}
/*
* Hardware irqs have vectors starting at this offset.
*/
#define IA64_HARDWARE_IRQ_BASE 0x20
struct ia64_intrhand {
int (*ih_func)(void *);
void *ih_arg;
LIST_ENTRY(ia64_intrhand) ih_q;
int ih_level;
int ih_irq;
};
struct ia64_intr {
u_int irq;
struct sapic *sapic;
int type;
LIST_HEAD(, ia64_intrhand) intr_q;
char evname[32];
struct evcnt evcnt;
};
static struct ia64_intr *ia64_intrs[256];
static void
ia64_intr_eoi(void *arg)
{
u_int vector = (uintptr_t)arg;
struct ia64_intr *i;
i = ia64_intrs[vector];
if (i != NULL)
sapic_eoi(i->sapic, vector);
}
static void
ia64_intr_mask(void *arg)
{
u_int vector = (uintptr_t)arg;
struct ia64_intr *i;
i = ia64_intrs[vector];
if (i != NULL) {
sapic_mask(i->sapic, i->irq);
sapic_eoi(i->sapic, vector);
}
}
#if 0
static void
ia64_intr_unmask(void *arg)
{
u_int vector = (uintptr_t)arg;
struct ia64_intr *i;
i = ia64_intrs[vector];
if (i != NULL)
sapic_unmask(i->sapic, i->irq);
}
#endif
void *
intr_establish_xname(int irq, int type, int level, int (*func)(void *),
void *arg, const char *xname)
{
/* TODO: xname support */
return intr_establish(irq, type, level, func, arg);
}
void *
intr_establish(int irq, int type, int level, int (*func)(void *), void *arg)
{
struct ia64_intr *i;
struct ia64_intrhand *ih;
struct sapic *sa;
u_int vector;
/* Get the I/O SAPIC that corresponds to the IRQ. */
sa = sapic_lookup(irq);
if (sa == NULL)
return NULL;
switch (type) {
case IST_EDGE:
case IST_LEVEL:
break;
default:
return NULL;
}
/*
* XXX - There's a priority implied by the choice of vector.
* We should therefore relate the vector to the interrupt type.
*/
vector = irq + IA64_HARDWARE_IRQ_BASE;
i = ia64_intrs[vector];
if (i == NULL) {
i = kmem_alloc(sizeof(struct ia64_intr), KM_SLEEP);
i->irq = irq;
i->sapic = sa;
i->type = type;
LIST_INIT(&i->intr_q);
snprintf(i->evname, sizeof(i->evname), "irq %d", irq);
evcnt_attach_dynamic(&i->evcnt, EVCNT_TYPE_INTR, NULL,
"iosapic", i->evname);
ia64_intrs[vector] = i;
sapic_config_intr(irq, type);
sapic_enable(i->sapic, irq, vector);
} else
if (i->type != type)
return NULL;
ih = kmem_alloc(sizeof(*ih), KM_SLEEP);
ih->ih_func = func;
ih->ih_arg = arg;
ih->ih_level = level;
ih->ih_irq = irq;
LIST_INSERT_HEAD(&i->intr_q, ih, ih_q);
return ih;
}
void
intr_disestablish(void *cookie)
{
struct ia64_intr *i;
struct ia64_intrhand *ih = cookie;
u_int vector = ih->ih_irq + IA64_HARDWARE_IRQ_BASE;
i = ia64_intrs[vector];
LIST_REMOVE(ih, ih_q);
if (LIST_FIRST(&i->intr_q) == NULL) {
ia64_intr_mask((void *)(uintptr_t)vector);
ia64_intrs[vector] = NULL;
evcnt_detach(&i->evcnt);
kmem_free(i, sizeof(*i));
}
kmem_free(ih, sizeof(*ih));
}
static int
ia64_dispatch_intr(void *frame, u_int vector)
{
struct ia64_intr *i;
struct ia64_intrhand *ih;
int handled = 0;
/*
* Find the interrupt thread for this vector.
*/
i = ia64_intrs[vector];
KASSERT(i != NULL);
i->evcnt.ev_count++;
LIST_FOREACH(ih, &i->intr_q, ih_q) {
if (__predict_false(ih->ih_func == NULL))
printf("%s: spurious interrupt (irq = %d)\n",
__func__, ih->ih_irq);
else if (__predict_true((*ih->ih_func)(ih->ih_arg)))
handled = 1;
}
ia64_intr_eoi((void *)(uintptr_t)vector);
return handled;
}
void
ia64_handle_intr(void *tf)
{
panic("XXX %s not implemented", __func__);
}
#ifdef DDB
void
db_print_vector(u_int vector, int always)
{
struct ia64_intr *i;
i = ia64_intrs[vector];
if (i != NULL) {
db_printf("vector %u (%p): ", vector, i);
sapic_print(i->sapic, i->irq);
} else if (always)
db_printf("vector %u: unassigned\n", vector);
}
const char *
intr_string(intr_handle_t ih, char *buf, size_t len)
{
panic("XXX %s not implemented", __func__);
}
#endif