/* $NetBSD: pci_a12.c,v 1.4 1998/04/24 01:25:18 mjacob Exp $ */ /* [Notice revision 2.0] * Copyright (c) 1997, 1998 Avalon Computer Systems, Inc. * All rights reserved. * * Author: Ross Harvey * * 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 and * author 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. * 3. Neither the name of Avalon Computer Systems, Inc. nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * 4. This copyright will be assigned to The NetBSD Foundation on * 1/1/2000 unless these terms (including possibly the assignment * date) are updated in writing by Avalon prior to the latest specified * assignment date. * * THIS SOFTWARE IS PROVIDED BY AVALON COMPUTER SYSTEMS, INC. AND CONTRIBUTORS * ``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 FOUNDATION OR CONTRIBUTORS * 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 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 "opt_avalon_a12.h" /* Config options headers */ #include /* RCS ID & Copyright macro defns */ __KERNEL_RCSID(0, "$NetBSD: pci_a12.c,v 1.4 1998/04/24 01:25:18 mjacob Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef EVCNT_COUNTERS #include #endif #define PCI_A12() /* Generate ctags(1) key */ #define LOADGSR() (REGVAL(A12_GSR) & 0x7fc0) static int pci_serr __P((void *)); static int a12_xbar_flag __P((void *)); struct a12_intr_vect_t { int on; int (*f) __P((void *)); void *a; } a12_intr_pci = { 0 }, a12_intr_serr = { 1, pci_serr }, a12_intr_flag = { 1, a12_xbar_flag }, a12_intr_iei = { 0 }, a12_intr_dc = { 0 }, a12_intr_xb = { 0 }; static struct gintcall { char flag; char needsclear; char intr_index; /* XXX implicit crossref */ struct a12_intr_vect_t *f; char *msg; } gintcall[] = { { 6, 0, 2, &a12_intr_pci, "PCI Device" }, { 7, 1, 3, &a12_intr_serr, "PCI SERR#" }, { 8, 1, 4, &a12_intr_flag, "XB Frame MCE" }, { 9, 1, 5, &a12_intr_flag, "XB Frame ECE" }, /* skip 10, gsr.TEI */ { 11, 1, 6, &a12_intr_iei, "Interval Timer"}, { 12, 1, 7, &a12_intr_flag, "XB FIFO Overrun"}, { 13, 1, 8, &a12_intr_flag, "XB FIFO Underrun"}, { 14, 1, 9, &a12_intr_dc, "DC Control Word"}, { 0 } }; #ifdef EVCNT_COUNTERS struct evcnt a12_intr_evcnt; #endif int avalon_a12_intr_map __P((void *, pcitag_t, int, int, pci_intr_handle_t *)); const char *avalon_a12_intr_string __P((void *, pci_intr_handle_t)); void *avalon_a12_intr_establish __P((void *, pci_intr_handle_t, int, int (*func)(void *), void *)); void avalon_a12_intr_disestablish __P((void *, void *)); static void clear_gsr_interrupt __P((long)); static void a12_GInt(void); void a12_iointr __P((void *framep, unsigned long vec)); void pci_a12_pickintr(ccp) struct a12c_config *ccp; { pci_chipset_tag_t pc = &ccp->ac_pc; pc->pc_intr_v = ccp; pc->pc_intr_map = avalon_a12_intr_map; pc->pc_intr_string = avalon_a12_intr_string; pc->pc_intr_establish = avalon_a12_intr_establish; pc->pc_intr_disestablish = avalon_a12_intr_disestablish; /* Not supported on A12. */ pc->pc_pciide_compat_intr_establish = NULL; set_iointr(a12_iointr); } int avalon_a12_intr_map(ccv, bustag, buspin, line, ihp) void *ccv; pcitag_t bustag; int buspin, line; pci_intr_handle_t *ihp; { /* only one PCI slot (per CPU, that is, but there are 12 CPU's!) */ *ihp = 0; return 0; } const char * avalon_a12_intr_string(ccv, ih) void *ccv; pci_intr_handle_t ih; { return "a12 pci irq"; /* see "only one" note above */ } void * avalon_a12_intr_establish(ccv, ih, level, func, arg) void *ccv, *arg; pci_intr_handle_t ih; int level; int (*func) __P((void *)); { a12_intr_pci.f = func; a12_intr_pci.a = arg; a12_intr_pci.on= 1; alpha_wmb(); REGVAL(A12_OMR) |= A12_OMR_PCI_ENABLE; alpha_mb(); return (void *)func; } void avalon_a12_intr_disestablish(ccv, cookie) void *ccv, *cookie; { if(cookie == a12_intr_pci.f) { alpha_wmb(); REGVAL(A12_OMR) &= ~A12_OMR_PCI_ENABLE; alpha_mb(); a12_intr_pci.f = 0; a12_intr_pci.on= 0; } else What(); } void a12_intr_register_xb(f) int (*f) __P((void *)); { a12_intr_xb.f = f; a12_intr_xb.on = 1; } void a12_intr_register_icw(f) int (*f) __P((void *)); { long t; t = REGVAL(A12_OMR) & ~A12_OMR_ICW_ENABLE; if ((a12_intr_dc.on = (f != NULL)) != 0) t |= A12_OMR_ICW_ENABLE; a12_intr_dc.f = f; alpha_wmb(); REGVAL(A12_OMR) = t; alpha_mb(); } long a12_nothing; static void clear_gsr_interrupt(write_1_to_clear) long write_1_to_clear; { REGVAL(A12_GSR) = write_1_to_clear; alpha_mb(); a12_nothing = REGVAL(A12_GSR); } static int pci_serr(p) void *p; { panic("pci_serr"); } static int a12_xbar_flag(p) void *p; { panic("a12_xbar_flag: %s", p); } static void a12_GInt(void) { struct gintcall *gic; long gsrsource, gsrvalue = LOADGSR(); void *s; /* * Switch interrupts do not go through this function */ for(gic=gintcall; gic->f; ++gic) { gsrsource = gsrvalue & 1L<flag; if (gsrsource && gic->f->on) { #ifndef EVCNT_COUNTERS if (gic->intr_index >= INTRCNT_A12_IRQ_LEN) panic("A12 INTRCNT"); intrcnt[INTRCNT_A12_IRQ + gic->intr_index]; #endif if(gic->needsclear) clear_gsr_interrupt(1L<flag); s = gic->f->a; if (s == NULL) s = gic->msg; if (gic->f->f == NULL) printf("Stray interrupt: %s OMR=%lx\n", gic->msg, REGVAL(A12_OMR) & 0xffc0); else gic->f->f(s); alpha_wmb(); } } } /* * IRQ_H 3 2 1 0 * EV5(10) 23 22 21 20 * EV5(16) 17 16 15 14 * OSF IPL 6 5 4 3 * VECTOR(16) 940 930 920 900 note: no 910 or 940 * f(VECTOR) 4 3 2 0 note: no 1 or 4 * EVENT Never Clk Misc Xbar */ void a12_iointr(framep, vec) void *framep; unsigned long vec; { unsigned irq = (vec-0x900) >> 4; /* this is the f(vector) above */ /* * Xbar device is in the A12 CPU core logic, so its interrupts * might as well be hardwired. */ #ifdef EVCNT_COUNTERS ++a12_intr_evcnt.ev_count; #else /* XXX implicit knowledge of intrcnt[] */ if(irq < 2) ++intrcnt[INTRCNT_A12_IRQ + irq]; #if 2 >= INTRCNT_A12_IRQ_LEN #error INTRCNT_A12_IRQ_LEN inconsistent with respect to intrcnt[] use #endif #endif switch(irq) { case 0: if (a12_intr_xb.f == NULL) panic("no switch interrupt registered"); else a12_intr_xb.f(NULL); return; case 2: a12_GInt(); return; default: panic("a12_iointr"); } }