/* $OpenBSD: intr.c,v 1.54 2026/03/08 17:07:31 deraadt Exp $ */ /* * Copyright (c) 2002-2004 Michael Shalayeff * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include struct hppa_iv intr_store[8*2*CPU_NINTS] __attribute__ ((aligned(32))), *intr_more = intr_store, *intr_list; struct hppa_iv intr_table[CPU_NINTS] __attribute__ ((aligned(32))) = { { IPL_SOFTCLOCK, 0, HPPA_IV_SOFT, 0, 0, NULL }, { IPL_SOFTNET , 0, HPPA_IV_SOFT, 0, 0, NULL }, { 0 }, { 0 }, { IPL_SOFTTTY , 0, HPPA_IV_SOFT, 0, 0, NULL } }; volatile u_long imask[NIPL] = { 0, 1 << (IPL_SOFTCLOCK - 1), 1 << (IPL_SOFTNET - 1), 0, 0, 1 << (IPL_SOFTTTY - 1) }; #ifdef DIAGNOSTIC void splassert_check(int wantipl, const char *func) { struct cpu_info *ci = curcpu(); if (ci->ci_cpl < wantipl) splassert_fail(wantipl, ci->ci_cpl, func); } #endif void cpu_intr_init(void) { struct cpu_info *ci = curcpu(); struct hppa_iv *iv; int level, bit; u_long mask; mask = ci->ci_mask | SOFTINT_MASK; /* map the shared ints */ while (intr_list) { iv = intr_list; intr_list = iv->next; bit = ffs(imask[(int)iv->pri]); if (!bit--) { bit = ffs(~mask); if (!bit--) panic("cpu_intr_init: out of bits"); iv->next = NULL; iv->bit = 1 << bit; intr_table[bit] = *iv; mask |= (1 << bit); imask[(int)iv->pri] |= (1 << bit); } else { iv->bit = 1 << bit; iv->next = intr_table[bit].next; intr_table[bit].next = iv; } } for (level = 0; level < NIPL - 1; level++) imask[level + 1] |= imask[level]; /* XXX the whacky trick is to prevent hardclock from happening */ mfctl(CR_ITMR, mask); mtctl(mask - 1, CR_ITMR); mtctl(ci->ci_mask, CR_EIEM); /* ack the unwanted interrupts */ mfctl(CR_EIRR, mask); mtctl(mask & (1U << 31), CR_EIRR); /* in spl*() we trust, clock is started in initclocks() */ ci->ci_psw |= PSL_I; ssm(PSL_I, mask); } /* * Find an available, non-shared interrupt bit. * Returns -1 if all interrupt bits are in use. */ int cpu_intr_findirq(void) { int irq; for (irq = 0; irq < CPU_NINTS; irq++) if (intr_table[irq].handler == NULL && intr_table[irq].pri == 0) return irq; return -1; } void * cpu_intr_map(void *v, int pri, int irq, int (*handler)(void *), void *arg, const char *name) { struct hppa_iv *iv, *pv = v, *ivb = pv->next; struct evcount *cnt; if (irq < 0 || irq >= CPU_NINTS) return (NULL); cnt = (struct evcount *)malloc(sizeof *cnt, M_DEVBUF, M_NOWAIT); if (!cnt) return (NULL); iv = &ivb[irq]; if (iv->handler) { if (!pv->share) { free(cnt, M_DEVBUF, sizeof *cnt); return (NULL); } else { iv = pv->share; pv->share = iv->share; iv->share = ivb[irq].share; ivb[irq].share = iv; } } evcount_attach(cnt, name, NULL); iv->pri = pri; iv->irq = irq; iv->flags = 0; iv->handler = handler; iv->arg = arg; iv->cnt = cnt; iv->next = intr_list; intr_list = iv; return (iv); } void * cpu_intr_establish(int pri, int irq, int (*handler)(void *), void *arg, const char *name) { struct cpu_info *ci = curcpu(); struct hppa_iv *iv, *ev; struct evcount *cnt; if (irq < 0 || irq >= CPU_NINTS || intr_table[irq].handler) return (NULL); if ((intr_table[irq].flags & HPPA_IV_SOFT) != 0) return (NULL); cnt = (struct evcount *)malloc(sizeof *cnt, M_DEVBUF, M_NOWAIT); if (!cnt) return (NULL); ci->ci_mask |= (1 << irq); imask[pri] |= (1 << irq); iv = &intr_table[irq]; iv->pri = pri; iv->irq = irq; iv->bit = 1 << irq; iv->flags = 0; iv->handler = handler; iv->arg = arg; iv->cnt = cnt; iv->next = NULL; iv->share = NULL; if (pri == IPL_NESTED) { iv->flags = HPPA_IV_CALL; iv->next = intr_more; intr_more += 2 * CPU_NINTS; for (ev = iv->next + CPU_NINTS; ev < intr_more; ev++) ev->share = iv->share, iv->share = ev; free(cnt, M_DEVBUF, sizeof *cnt); iv->cnt = NULL; } else if (name == NULL) { free(cnt, M_DEVBUF, sizeof *cnt); iv->cnt = NULL; } else evcount_attach(cnt, name, NULL); return (iv); } void cpu_intr(void *v) { struct cpu_info *ci = curcpu(); struct trapframe *frame = v; struct hppa_iv *iv; int pri, r, s, bit; u_long mask; void *arg; mtctl(0, CR_EIEM); s = ci->ci_cpl; if (ci->ci_idepth++) frame->tf_flags |= TFF_INTR; /* Process higher priority interrupts first. */ for (pri = NIPL - 1; pri > s; pri--) { mask = imask[pri] ^ imask[pri - 1]; while (ci->ci_ipending & mask) { bit = fls(ci->ci_ipending & mask) - 1; iv = &intr_table[bit]; ci->ci_ipending &= ~(1L << bit); if (iv->flags & HPPA_IV_CALL) continue; /* handled in locore */ atomic_inc_int(&uvmexp.intrs); if (iv->flags & HPPA_IV_SOFT) atomic_inc_int(&uvmexp.softs); ci->ci_cpl = iv->pri; mtctl(frame->tf_eiem, CR_EIEM); #ifdef MULTIPROCESSOR if (pri < IPL_CLOCK) __mp_lock(&kernel_lock); #endif for (r = iv->flags & HPPA_IV_SOFT; iv && iv->handler; iv = iv->next) { /* no arg means pass the frame */ arg = iv->arg ? iv->arg : v; if ((iv->handler)(arg) == 1) { if (iv->cnt) iv->cnt->ec_count++; r |= 1; } } #if 0 /* XXX this does not work, lasi gives us double ints */ if (!r) { ci->ci_cpl = 0; printf("stray interrupt %d\n", bit); } #endif #ifdef MULTIPROCESSOR if (pri < IPL_CLOCK) __mp_unlock(&kernel_lock); #endif mtctl(0, CR_EIEM); } } ci->ci_idepth--; ci->ci_cpl = s; mtctl(frame->tf_eiem, CR_EIEM); } void intr_barrier(void *cookie) { sched_barrier(NULL); } void * softintr_establish(int pri, void (*handler)(void *), void *arg) { struct hppa_iv *iv; int irq; if (pri == IPL_TTY) pri = IPL_SOFTTTY; irq = pri - 1; iv = &intr_table[irq]; if ((iv->flags & HPPA_IV_SOFT) == 0 || iv->pri != pri) return (NULL); if (iv->handler) { struct hppa_iv *nv; nv = malloc(sizeof *iv, M_DEVBUF, M_NOWAIT); if (!nv) return (NULL); while (iv->next) iv = iv->next; iv->next = nv; iv = nv; } else imask[pri] |= (1 << irq); iv->pri = pri; iv->irq = 0; iv->bit = 1 << irq; iv->flags = HPPA_IV_SOFT; iv->handler = (int (*)(void *))handler; /* XXX */ iv->arg = arg; iv->cnt = NULL; iv->next = NULL; iv->share = NULL; return (iv); } void softintr_disestablish(void *cookie) { struct hppa_iv *iv = cookie; int irq = iv->pri - 1; if (&intr_table[irq] == cookie) { if (iv->next) { struct hppa_iv *nv = iv->next; iv->handler = nv->handler; iv->arg = nv->arg; iv->next = nv->next; free(nv, M_DEVBUF, sizeof *nv); return; } else { iv->handler = NULL; iv->arg = NULL; return; } } for (iv = &intr_table[irq]; iv; iv = iv->next) { if (iv->next == cookie) { iv->next = iv->next->next; free(cookie, M_DEVBUF, 0); return; } } } void softintr_schedule(void *cookie) { struct hppa_iv *iv = cookie; atomic_setbits_long(&curcpu()->ci_ipending, 1 << (iv->pri - 1)); }