root/sys/arch/ia64/ia64/interrupt.c
/* $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  Software.Distribution@CS.CMU.EDU
 *  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