diff options
author | Per Fogelstrom <pefo@cvs.openbsd.org> | 1997-10-11 11:53:01 +0000 |
---|---|---|
committer | Per Fogelstrom <pefo@cvs.openbsd.org> | 1997-10-11 11:53:01 +0000 |
commit | cceea337e96cd164370f0df98bb0a4e390cd986e (patch) | |
tree | 40a28ca896eaef5a8f6a4f38ca08d1d2fc61a953 /sys/arch/powerpc | |
parent | 4e38517cf8d5704c4f0b0a60638dd6f1c7e55828 (diff) |
Monolithic PowerPC kernel ISA bus support
Diffstat (limited to 'sys/arch/powerpc')
-rw-r--r-- | sys/arch/powerpc/isa/isa_machdep.h | 93 | ||||
-rw-r--r-- | sys/arch/powerpc/isa/isabus.c | 522 |
2 files changed, 615 insertions, 0 deletions
diff --git a/sys/arch/powerpc/isa/isa_machdep.h b/sys/arch/powerpc/isa/isa_machdep.h new file mode 100644 index 00000000000..bb2ad834b43 --- /dev/null +++ b/sys/arch/powerpc/isa/isa_machdep.h @@ -0,0 +1,93 @@ +/* $OpenBSD: isa_machdep.h,v 1.1 1997/10/11 11:52:59 pefo Exp $ */ + +/* + * Copyright (c) 1997 Per Fogelstrom + * + * 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 under OpenBSD by + * Per Fogelstrom, Opsycon AB, Sweden. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * 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 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. + * + */ +#ifndef _ISA_MACHDEP_H_ +#define _ISA_MACHDEP_H_ + +typedef struct p4e_isa_bus *isa_chipset_tag_t; + +/* + * I/O macros to access isa bus ports/memory. + * At the first glance theese macros may seem inefficient. + * However, the cpu executes an instruction every <10 ns + * so the bus is much slower so it doesn't matter, really. + */ +#define isa_outb(x,y) outb(p4e_isa_io.bus_base + (x), y) +#define isa_inb(x) inb(p4e_isa_io.bus_base + (x)) + +struct p4e_isa_bus { + void *ic_data; + + void (*ic_attach_hook) __P((struct device *, struct device *, + struct isabus_attach_args *)); + void *(*ic_intr_establish) __P((isa_chipset_tag_t, int, int, int, + int (*)(void *), void *, char *)); + void (*ic_intr_disestablish) __P((isa_chipset_tag_t, void *)); +}; + + +/* + * Functions provided to machine-independent ISA code. + */ +#define isa_attach_hook(p, s, a) /* \ + (*(a)->iba_ic->ic_attach_hook)((p), (s), (a)) */ +#define isa_intr_establish(c, i, t, l, f, a, w) \ + (*(c)->ic_intr_establish)((c)->ic_data, (i), (t), (l), (f), (a), (w)) +#define isa_intr_disestablish(c, h) \ + (*(c)->ic_intr_disestablish)((c)->ic_data, (h)) + + +/* + * Interrupt control struct used to control the ICU setup. + */ + +struct intrhand { + struct intrhand *ih_next; + int (*ih_fun) __P((void *)); + void *ih_arg; + u_long ih_count; + int ih_level; + int ih_irq; + char *ih_what; +}; + +#define ICU_LEN 16 /* Number of possible interrupt sources */ + +/* + * Let com.c know where our console is! + */ +#define CONADDR (0x2e8) +#define COM_FREQ (1843200 / 3) /* Clocked with 8Mhz instead of 24! */ + +extern void * isabr_intr_establish(isa_chipset_tag_t, int, int, int, int (*ih_fun) __P((void *)), void *, char *); +#endif /* _ISA_MACHDEP_H_ */ diff --git a/sys/arch/powerpc/isa/isabus.c b/sys/arch/powerpc/isa/isabus.c new file mode 100644 index 00000000000..1753fa866f6 --- /dev/null +++ b/sys/arch/powerpc/isa/isabus.c @@ -0,0 +1,522 @@ +/* $OpenBSD: isabus.c,v 1.1 1997/10/11 11:53:00 pefo Exp $ */ +/* $NetBSD: isa.c,v 1.33 1995/06/28 04:30:51 cgd Exp $ */ + +/*- + * Copyright (c) 1995 Per Fogelstrom + * Copyright (c) 1993, 1994 Charles Hannum. + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz and Don Ahn. + * + * 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/12/91 + */ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * 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 Mellon + * the rights to redistribute these changes. + */ +/* + Copyright 1988, 1989 by Intel Corporation, Santa Clara, California. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Intel +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + +INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include <sys/param.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/malloc.h> + +#include <machine/pte.h> +#include <machine/cpu.h> +#include <machine/pio.h> +#include <machine/autoconf.h> +#include <machine/intr.h> + +#include <dev/isa/isareg.h> +#include <dev/isa/isavar.h> + +static int beeping; + +#define IRQ_SLAVE 2 + +struct isabr_softc { + struct device sc_dv; + struct p4e_isa_bus p4e_isa_cs; + struct bushook sc_bus; +}; + +/* Definition of the driver for autoconfig. */ +int isabrmatch(struct device *, void *, void *); +void isabrattach(struct device *, struct device *, void *); +int isabrprint(void *, const char *); + +struct cfattach isabr_ca = { + sizeof(struct isabr_softc), isabrmatch, isabrattach +}; +struct cfdriver isabr_cd = { + NULL, "isabr", DV_DULL, NULL, 0 +}; + +void *isabr_intr_establish __P((isa_chipset_tag_t, int, int, int, + int (*)(void *), void *, char *)); +void isabr_intr_disestablish __P((isa_chipset_tag_t, void*)); +void isabr_iointr __P((unsigned int, struct clockframe *)); +void isabr_initicu __P((void)); +void intr_calculatemasks __P((void)); + +struct p4e_bus_space p4e_isa_io = { + 0x80000000, 1 +}; + +struct p4e_bus_space p4e_isa_mem = { + 0xc0000000, 1 +}; + +int +isabrmatch(parent, cfdata, aux) + struct device *parent; + void *cfdata; + void *aux; +{ + struct confargs *ca = aux; + + /* Make sure that we're looking for a ISABR. */ + if (strcmp(ca->ca_name, isabr_cd.cd_name) != 0) + return (0); + + return (1); +} + +void +isabrattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct isabr_softc *sc = (struct isabr_softc *)self; + struct isabus_attach_args iba; + + printf("\n"); + + /* Initialize interrupt controller */ + isabr_initicu(); + + /* set up interrupt handlers */ + +/*XXX we may remove the bushook part of the softc struct... */ + sc->sc_bus.bh_dv = (struct device *)sc; + sc->sc_bus.bh_type = BUS_ISABR; + + sc->p4e_isa_cs.ic_intr_establish = isabr_intr_establish; + sc->p4e_isa_cs.ic_intr_disestablish = isabr_intr_disestablish; + + iba.iba_busname = "isa"; + iba.iba_iot = (bus_space_tag_t)&p4e_isa_io; + iba.iba_memt = (bus_space_tag_t)&p4e_isa_mem; + iba.iba_ic = &sc->p4e_isa_cs; + config_found(self, &iba, isabrprint); +} + +int +isabrprint(aux, pnp) + void *aux; + const char *pnp; +{ + struct confargs *ca = aux; + + if (pnp) + printf("%s at %s", ca->ca_name, pnp); + printf(" isa_io_base 0x%lx isa_mem_base 0x%lx", + p4e_isa_io.bus_base, p4e_isa_mem.bus_base); + return (UNCONF); +} + + +/* + * Interrupt system driver code + * ============================ + */ +#define LEGAL_IRQ(x) ((x) >= 0 && (x) < ICU_LEN && (x) != 2) + +int imen = 0xffffffff; +int intrtype[ICU_LEN], intrmask[ICU_LEN], intrlevel[ICU_LEN]; +struct intrhand *intrhand[ICU_LEN]; + +int fakeintr(void *a) {return 0;} + +/* + * 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 intrhand *q; + + /* First, figure out which levels each IRQ uses. */ + for (irq = 0; irq < ICU_LEN; irq++) { + register 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 < 5; level++) { + register int irqs = 0; + for (irq = 0; irq < ICU_LEN; irq++) + if (intrlevel[irq] & (1 << level)) + irqs |= 1 << irq; + imask[level] = irqs | SINT_MASK; + } + + /* + * 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_NET] | imask[IPL_BIO]; + + /* + * Enforce a hierarchy that gives slow devices a better chance at not + * dropping data. + */ + imask[IPL_TTY] |= imask[IPL_NET] | imask[IPL_BIO]; + imask[IPL_NET] |= imask[IPL_BIO]; + + /* + * These are pseudo-levels. + */ + imask[IPL_NONE] = 0x00000000; + imask[IPL_HIGH] = 0xffffffff; + + /* And eventually calculate the complete masks. */ + for (irq = 0; irq < ICU_LEN; irq++) { + register int irqs = 1 << irq; + for (q = intrhand[irq]; q; q = q->ih_next) + irqs |= imask[q->ih_level]; + intrmask[irq] = irqs | SINT_MASK; + } + + /* Lastly, determine which IRQs are actually in use. */ + { + register 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; + isa_outb(IO_ICU1 + 1, imen); + isa_outb(IO_ICU2 + 1, imen >> 8); + } +} + +/* + * Establish a ISA bus interrupt. + */ +void * +isabr_intr_establish(ic, irq, type, level, ih_fun, ih_arg, ih_what) + isa_chipset_tag_t ic; + int irq; + int type; + int level; + int (*ih_fun) __P((void *)); + void *ih_arg; + char *ih_what; +{ + struct intrhand **p, *q, *ih; + static struct intrhand fakehand = {NULL, fakeintr}; + extern int cold; + +static int inthnd_installed = 0; + + if(!inthnd_installed) { + install_extint(isabr_iointr); + inthnd_installed++; + } + + /* 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_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_fun = ih_fun; + ih->ih_arg = ih_arg; + ih->ih_count = 0; + ih->ih_next = NULL; + ih->ih_level = level; + ih->ih_irq = irq; + ih->ih_what = ih_what; + *p = ih; + + return (ih); +} + +void +isabr_intr_disestablish(ic, arg) + isa_chipset_tag_t ic; + void *arg; +{ + +} + +void +do_pending_int() +{ + struct intrhand *ih; + int vector; + int pcpl; + int hwpend; + int emsr, dmsr; +static int processing; + + if(processing) + return; + + processing = 1; + __asm__ volatile("mfmsr %0" : "=r"(emsr)); + dmsr = emsr & ~PSL_EE; + __asm__ volatile("mtmsr %0" :: "r"(dmsr)); + + pcpl = splhigh(); /* Turn off all */ + hwpend = ipending & ~pcpl; /* Do now unmasked pendings */ + hwpend &= ((1L << ICU_LEN) - 1); + imen &= ~hwpend; + while(hwpend) { + vector = ffs(hwpend) - 1; + hwpend &= ~(1L << vector); + ih = intrhand[vector]; + while(ih) { + (*ih->ih_fun)(ih->ih_arg); + ih = ih->ih_next; + } + } + if(ipending & SINT_CLOCK) { + ipending &= ~SINT_CLOCK; + softclock(); + } + if(ipending & SINT_NET) { + extern int netisr; + int pisr = netisr; + netisr = 0; + ipending &= ~SINT_NET; + softnet(pisr); + } + ipending &= pcpl; + cpl = pcpl; /* Don't use splx... we are here already! */ + __asm__ volatile("mtmsr %0" :: "r"(emsr)); + processing = 0; + + isa_outb(IO_ICU1 + 1, imen); + isa_outb(IO_ICU2 + 1, imen >> 8); +} + +/* + * Process an interrupt from the ISA bus. + * When we get here remember we have "delayed" ipl mask + * settings from the spl<foo>() calls. Yes it's faster + * to do it like this because SPL's are done so frequently + * and interrupts are likely to *NOT* happen most of the + * times the spl level is changed. + */ +void +isabr_iointr(mask, cf) + unsigned mask; + struct clockframe *cf; +{ + struct intrhand *ih; + int isa_vector; + int o_imen, r_imen; + char vector; + int pcpl; + + pcpl = splhigh() ; /* Turn off all */ + isa_outb(IO_ICU1, 0x0f); /* Poll */ + vector = isa_inb(IO_ICU1); + if((isa_vector = vector & 7) == 2) { + isa_outb(IO_ICU2, 0x0f); + vector = isa_inb(IO_ICU2); + isa_vector = (vector & 7) | 8; + } + + o_imen = imen; + r_imen = 1 << (isa_vector & (ICU_LEN - 1)); + imen |= r_imen; +#if 0 /* XXX I'm not sure which method to prefere... */ + if(isa_vector & 0x08) { + isa_inb(IO_ICU2 + 1); + isa_outb(IO_ICU2 + 1, imen >> 8); + isa_outb(IO_ICU2, 0x60 + (isa_vector & 7)); + isa_outb(IO_ICU1, 0x60 + IRQ_SLAVE); + } + else { + isa_inb(IO_ICU1 + 1); + isa_outb(IO_ICU1 + 1, imen); + isa_outb(IO_ICU1, 0x60 + isa_vector); + } +#else + isa_outb(IO_ICU1, 0x20); + isa_outb(IO_ICU2, 0x20); + isa_outb(IO_ICU1 + 1, imen); + isa_outb(IO_ICU2 + 1, imen >> 8); +#endif + if((pcpl & r_imen) != 0) { + ipending |= r_imen; /* Masked! Mark this as pending */ + } + else { + ih = intrhand[isa_vector]; + if(ih == NULL) + printf("isa: spurious interrupt %d\n", isa_vector); + + while(ih) { + (*ih->ih_fun)(ih->ih_arg); + ih = ih->ih_next; + } + imen = o_imen; + } + isa_inb(IO_ICU1 + 1); + isa_inb(IO_ICU2 + 1); + isa_outb(IO_ICU1 + 1, imen); + isa_outb(IO_ICU2 + 1, imen >> 8); + + splx(pcpl); /* Process pendings. */ +} + + +/* + * Initialize the Interrupt controller logic. + */ +void +isabr_initicu() +{ + + isa_outb(0x04d0, 0); /* Clear level int mask 0-7 */ + isa_outb(0x04d1, 0); /* Clear level int mask 8-15 */ + + isa_outb(IO_ICU1, 0x11); /* program device, four bytes */ + isa_outb(IO_ICU1+1, 0); /* starting at this vector */ + isa_outb(IO_ICU1+1, 1 << IRQ_SLAVE); /* slave on line 2 */ + isa_outb(IO_ICU1+1, 1); /* 8086 mode */ + isa_outb(IO_ICU1+1, 0xff); /* leave interrupts masked */ + isa_outb(IO_ICU1, 0x68); /* special mask mode */ + isa_outb(IO_ICU1, 0x0a); /* Read IRR by default. */ + + isa_outb(IO_ICU2, 0x11); /* program device, four bytes */ + isa_outb(IO_ICU2+1, 8); /* staring at this vector */ + isa_outb(IO_ICU2+1, IRQ_SLAVE); + isa_outb(IO_ICU2+1, 1); /* 8086 mode */ + isa_outb(IO_ICU2+1, 0xff); /* leave interrupts masked */ + isa_outb(IO_ICU2, 0x68); /* special mask mode */ + isa_outb(IO_ICU2, 0x0a); /* Read IRR by default */ +} + |