/* $NetBSD: isa_cats_machdep.c,v 1.3 1999/03/19 04:58:45 cgd Exp $ */ /*- * Copyright (c) 1996-1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Mark Brinicombe, Charles M. Hannum and by Jason R. Thorpe of the * Numerical Aerospace Simulation Facility, NASA Ames Research Center. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, 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. */ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * @(#)isa.c 7.2 (Berkeley) 5/13/91 */ #include "opt_irqstats.h" #include #include #include #include #include #include #define _ARM32_BUS_DMA_PRIVATE #include #include #include #include #include #include #include #include #include #include #include #include #include "isadma.h" struct arm32_isa_chipset isa_chipset_tag; void isa_strayintr __P((int)); void intr_calculatemasks __P((void)); int fakeintr __P((void *)); int isa_irqdispatch __P((void *arg)); u_int imask[IPL_LEVELS]; unsigned imen; #ifdef IRQSTATS u_int isa_intr_count[ICU_LEN]; #endif /* IRQSTATS */ #define AUTO_EOI_1 #define AUTO_EOI_2 /* * Fill in default interrupt table (in case of spuruious interrupt * during configuration of kernel, setup interrupt control unit */ void isa_icu_init(void) { /* initialize 8259's */ outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ outb(IO_ICU1+1, ICU_OFFSET); /* starting at this vector index */ outb(IO_ICU1+1, 1 << IRQ_SLAVE); /* slave on line 2 */ #ifdef AUTO_EOI_1 outb(IO_ICU1+1, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU1+1, 1); /* 8086 mode */ #endif outb(IO_ICU1+1, 0xff); /* leave interrupts masked */ outb(IO_ICU1, 0x68); /* special mask mode (if available) */ outb(IO_ICU1, 0x0a); /* Read IRR by default. */ #ifdef REORDER_IRQ outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */ #endif outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ outb(IO_ICU2+1, ICU_OFFSET+8); /* staring at this vector index */ outb(IO_ICU2+1, IRQ_SLAVE); #ifdef AUTO_EOI_2 outb(IO_ICU2+1, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU2+1, 1); /* 8086 mode */ #endif outb(IO_ICU2+1, 0xff); /* leave interrupts masked */ outb(IO_ICU2, 0x68); /* special mask mode (if available) */ outb(IO_ICU2, 0x0a); /* Read IRR by default. */ } /* * Caught a stray interrupt, notify */ void isa_strayintr(irq) int irq; { static u_long strays; /* * Stray interrupts on irq 7 occur when an interrupt line is raised * and then lowered before the CPU acknowledges it. This generally * means either the device is screwed or something is cli'ing too * long and it's timing out. */ if (++strays <= 5) log(LOG_ERR, "stray interrupt %d%s\n", irq, strays >= 5 ? "; stopped logging" : ""); } int intrtype[ICU_LEN], intrmask[ICU_LEN], intrlevel[ICU_LEN]; struct irqhandler *intrhand[ICU_LEN]; /* * Recalculate the interrupt masks from scratch. * We could code special registry and deregistry versions of this function that * would be faster, but the code would be nastier, and we don't expect this to * happen very much anyway. */ void intr_calculatemasks() { int irq, level; struct irqhandler *q; /* First, figure out which levels each IRQ uses. */ for (irq = 0; irq < ICU_LEN; irq++) { int levels = 0; for (q = intrhand[irq]; q; q = q->ih_next) levels |= 1 << q->ih_level; intrlevel[irq] = levels; } /* Then figure out which IRQs use each level. */ for (level = 0; level < IPL_LEVELS; level++) { int irqs = 0; for (irq = 0; irq < ICU_LEN; irq++) if (intrlevel[irq] & (1 << level)) irqs |= 1 << irq; imask[level] = irqs; } /* * IPL_NONE is used for hardware interrupts that are never blocked, * and do not block anything else. */ imask[IPL_NONE] = 0; /* * Enforce a hierarchy that gives slow devices a better chance at not * dropping data. */ imask[IPL_BIO] |= imask[IPL_NONE]; imask[IPL_NET] |= imask[IPL_BIO]; imask[IPL_TTY] |= imask[IPL_NET]; /* * There are tty, network and disk drivers that use free() at interrupt * time, so imp > (tty | net | bio). */ imask[IPL_IMP] |= imask[IPL_TTY]; imask[IPL_AUDIO] |= imask[IPL_IMP]; /* * Since run queues may be manipulated by both the statclock and tty, * network, and disk drivers, clock > imp. */ imask[IPL_CLOCK] |= imask[IPL_AUDIO]; imask[IPL_CLOCK] |= imask[IPL_IMP]; /* * IPL_HIGH must block everything that can manipulate a run queue. */ imask[IPL_HIGH] |= imask[IPL_CLOCK]; /* * We need serial drivers to run at the absolute highest priority to * avoid overruns, so serial > high. */ imask[IPL_SERIAL] |= imask[IPL_HIGH]; /* And eventually calculate the complete masks. */ for (irq = 0; irq < ICU_LEN; irq++) { int irqs = 1 << irq; for (q = intrhand[irq]; q; q = q->ih_next) irqs |= imask[q->ih_level]; intrmask[irq] = irqs; } /* Lastly, determine which IRQs are actually in use. */ { int irqs = 0; for (irq = 0; irq < ICU_LEN; irq++) if (intrhand[irq]) irqs |= 1 << irq; if (irqs >= 0x100) /* any IRQs >= 8 in use */ irqs |= 1 << IRQ_SLAVE; imen = ~irqs; SET_ICUS(); } #if 0 printf("type\tmask\tlevel\thand\n"); for (irq = 0; irq < ICU_LEN; irq++) { printf("%x\t%04x\t%x\t%p\n", intrtype[irq], intrmask[irq], intrlevel[irq], intrhand[irq]); } for (level = 0; level < IPL_LEVELS; ++level) printf("%d: %08x\n", level, imask[level]); #endif } int fakeintr(arg) void *arg; { return 0; } #define LEGAL_IRQ(x) ((x) >= 0 && (x) < ICU_LEN && (x) != 2) int isa_intr_alloc(ic, mask, type, irq) isa_chipset_tag_t ic; int mask; int type; int *irq; { int i, tmp, bestirq, count; struct irqhandler **p, *q; if (type == IST_NONE) panic("intr_alloc: bogus type"); bestirq = -1; count = -1; /* some interrupts should never be dynamically allocated */ mask &= 0xdef8; /* * XXX some interrupts will be used later (6 for fdc, 12 for pms). * the right answer is to do "breadth-first" searching of devices. */ mask &= 0xefbf; for (i = 0; i < ICU_LEN; i++) { if (LEGAL_IRQ(i) == 0 || (mask & (1<ih_next, tmp++) ; if ((bestirq == -1) || (count > tmp)) { bestirq = i; count = tmp; } break; case IST_PULSE: /* this just isn't shareable */ continue; } } if (bestirq == -1) return (1); *irq = bestirq; return (0); } /* * Set up an interrupt handler to start being called. * XXX PRONE TO RACE CONDITIONS, UGLY, 'INTERESTING' INSERTION ALGORITHM. */ void * isa_intr_establish(ic, irq, type, level, ih_fun, ih_arg) isa_chipset_tag_t ic; int irq; int type; int level; int (*ih_fun) __P((void *)); void *ih_arg; { struct irqhandler **p, *q, *ih; static struct irqhandler fakehand = {fakeintr}; extern int cold; /* printf("isa_intr_establish(%d, %d, %d)\n", irq, type, level);*/ /* no point in sleeping unless someone can free memory. */ ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK); if (ih == NULL) panic("isa_intr_establish: can't malloc handler info"); if (!LEGAL_IRQ(irq) || type == IST_NONE) panic("intr_establish: bogus irq or type"); switch (intrtype[irq]) { case IST_NONE: intrtype[irq] = type; /* printf("Setting irq %d to type %d - ", irq, type);*/ if (irq < 8) { outb(0x4d0, (inb(0x4d0) & ~(1 << irq)) | ((type == IST_LEVEL) ? (1 << irq) : 0)); /* printf("%02x\n", inb(0x4d0));*/ } else { outb(0x4d1, (inb(0x4d1) & ~(1 << irq)) | ((type == IST_LEVEL) ? (1 << irq) : 0)); /* printf("%02x\n", inb(0x4d1));*/ } break; case IST_EDGE: case IST_LEVEL: if (type == intrtype[irq]) break; case IST_PULSE: if (type != IST_NONE) panic("intr_establish: can't share %s with %s", isa_intr_typename(intrtype[irq]), isa_intr_typename(type)); break; } /* * Figure out where to put the handler. * This is O(N^2), but we want to preserve the order, and N is * generally small. */ for (p = &intrhand[irq]; (q = *p) != NULL; p = &q->ih_next) ; /* * Actually install a fake handler momentarily, since we might be doing * this with interrupts enabled and don't want the real routine called * until masking is set up. */ fakehand.ih_level = level; *p = &fakehand; intr_calculatemasks(); /* * Poke the real handler in now. */ ih->ih_func = ih_fun; ih->ih_arg = ih_arg; /* ih->ih_count = 0;*/ ih->ih_next = NULL; ih->ih_level = level; ih->ih_num = irq; *p = ih; return (ih); } /* * Deregister an interrupt handler. */ void isa_intr_disestablish(ic, arg) isa_chipset_tag_t ic; void *arg; { struct irqhandler *ih = arg; int irq = ih->ih_num; struct irqhandler **p, *q; if (!LEGAL_IRQ(irq)) panic("intr_disestablish: bogus irq"); /* * Remove the handler from the chain. * This is O(n^2), too. */ for (p = &intrhand[irq]; (q = *p) != NULL && q != ih; p = &q->ih_next) ; if (q) *p = q->ih_next; else panic("intr_disestablish: handler not registered"); free(ih, M_DEVBUF); intr_calculatemasks(); if (intrhand[irq] == NULL) intrtype[irq] = IST_NONE; } /* * isa_intr_init() * * Initialise the ISA ICU and attach an ISA interrupt handler to the * ISA interrupt line on the footbridge. */ void isa_intr_init(void) { static void *isa_ih; isa_icu_init(); isa_ih = intr_claim(IRQ_IN_L2, IPL_BIO, "isabus", isa_irqdispatch, NULL); } /* Static array of ISA DMA segments. We only have one on CATS */ #if NISADMA > 0 static bus_dma_segment_t isa_dma_segments[1]; #endif void isa_cats_init(iobase, membase) u_int iobase, membase; { #if NISADMA > 0 extern bus_dma_segment_t *pmap_isa_dma_ranges; extern int pmap_isa_dma_nranges; pmap_isa_dma_ranges = isa_dma_segments; pmap_isa_dma_nranges = 1; pmap_isa_dma_ranges[0].ds_addr = bootconfig.dram[0].address; pmap_isa_dma_ranges[0].ds_len = (16 * 1024 * 1024); #endif isa_io_init(iobase, membase); } void isa_attach_hook(parent, self, iba) struct device *parent, *self; struct isabus_attach_args *iba; { /* * Since we can only have one ISA bus, we just use a single * statically allocated ISA chipset structure. Pass it up * now. */ iba->iba_ic = &isa_chipset_tag; #if NISADMA > 0 isa_dma_init(); #endif } int isa_irqdispatch(arg) void *arg; { int irq; struct irqhandler *p; u_int iack; int res; iack = *((u_int *)(DC21285_PCI_IACK_VBASE)); iack &= 0xff; if (iack < 0x20 || iack > 0x2f) { printf("isa_irqdispatch: %x\n", iack); return(0); } irq = iack & 0x0f; #ifdef IRQSTATS ++isa_intr_count[irq]; #endif /* IRQSTATS */ p = intrhand[irq]; while (p) { #ifdef IRQSTATS /* ++p->ih_count;*/ #endif /* IRQSTATS */ res = p->ih_func(p->ih_arg); p = p->ih_next; } return(0); } void isa_fillw(val, addr, len) u_short val; void *addr; size_t len; { if ((u_int)addr >= isa_mem_data_vaddr() && (u_int)addr < isa_mem_data_vaddr() + 0x100000) { bus_size_t offset = ((u_int)addr) & 0xfffff; bus_space_set_region_2(&isa_mem_bs_tag, (bus_space_handle_t)isa_mem_bs_tag.bs_cookie, offset, val, len); } else { u_short *ptr = addr; while (len > 0) { *ptr++ = val; --len; } } }