/*- * Copyright (c) 1998 Doug Rabson * 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 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 AUTHOR 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) 1995, 1996 Carnegie-Mellon University. * All rights reserved. * * Author: 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) 1998 by Andrew Gallatin for Duke University */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define KV(pa) ALPHA_PHYS_TO_K0SEG(pa) static devclass_t apecs_devclass; static device_t apecs0; /* XXX only one for now */ struct apecs_softc { vm_offset_t dmem_base; /* dense memory */ vm_offset_t smem_base; /* sparse memory */ vm_offset_t io_base; /* dense i/o */ vm_offset_t cfg0_base; /* dense pci0 config */ vm_offset_t cfg1_base; /* dense pci1 config */ }; #define APECS_SOFTC(dev) (struct apecs_softc*) device_get_softc(dev) static alpha_chipset_read_hae_t apecs_read_hae; static alpha_chipset_write_hae_t apecs_write_hae; static alpha_chipset_t apecs_swiz_chipset = { apecs_read_hae, apecs_write_hae, }; /* * Memory functions. * * XXX linux does 32-bit reads/writes via dense space. This doesn't * appear to work for devices behind a ppb. I'm using sparse * accesses & they appear to work just fine everywhere. */ static u_int32_t apecs_hae_mem; #define REG1 (1UL << 24) static u_int32_t apecs_set_hae_mem(void *arg, u_int32_t pa) { int s; u_int32_t msb; if (pa >= REG1){ msb = pa & 0xf8000000; pa -= msb; s = splhigh(); if (msb != apecs_hae_mem) { apecs_hae_mem = msb; REGVAL(EPIC_HAXR1) = apecs_hae_mem; alpha_mb(); apecs_hae_mem = REGVAL(EPIC_HAXR1); } splx(s); } return pa; } static u_int64_t apecs_read_hae(void) { return apecs_hae_mem & 0xf8000000; } static void apecs_write_hae(u_int64_t hae) { u_int32_t pa = hae; apecs_set_hae_mem(0, pa); } static int apecs_probe(device_t dev); static int apecs_attach(device_t dev); static int apecs_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep); static int apecs_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie); static device_method_t apecs_methods[] = { /* Device interface */ DEVMETHOD(device_probe, apecs_probe), DEVMETHOD(device_attach, apecs_attach), /* Bus interface */ DEVMETHOD(bus_setup_intr, apecs_setup_intr), DEVMETHOD(bus_teardown_intr, apecs_teardown_intr), { 0, 0 } }; static driver_t apecs_driver = { "apecs", apecs_methods, sizeof(struct apecs_softc), }; #define APECS_SGMAP_BASE (8*1024*1024) #define APECS_SGMAP_SIZE (8*1024*1024) static void apecs_sgmap_invalidate(void) { alpha_mb(); REGVAL(EPIC_TBIA) = 0; alpha_mb(); } static void apecs_sgmap_map(void *arg, bus_addr_t ba, vm_offset_t pa) { u_int64_t *sgtable = arg; int index = alpha_btop(ba - APECS_SGMAP_BASE); if (pa) { if (pa > (1L<<32)) panic("apecs_sgmap_map: can't map address 0x%lx", pa); sgtable[index] = ((pa >> 13) << 1) | 1; } else { sgtable[index] = 0; } alpha_mb(); apecs_sgmap_invalidate(); } static void apecs_init_sgmap(void) { void *sgtable; /* * First setup Window 0 to map 8Mb to 16Mb with an * sgmap. Allocate the map aligned to a 32 boundary. */ REGVAL(EPIC_PCI_BASE_1) = APECS_SGMAP_BASE | EPIC_PCI_BASE_SGEN | EPIC_PCI_BASE_WENB; alpha_mb(); REGVAL(EPIC_PCI_MASK_1) = EPIC_PCI_MASK_8M; alpha_mb(); sgtable = contigmalloc(8192, M_DEVBUF, M_NOWAIT, 0, (1L<<34), 32*1024, (1L<<34)); if (!sgtable) panic("apecs_init_sgmap: can't allocate page table"); REGVAL(EPIC_TBASE_1) = (pmap_kextract((vm_offset_t) sgtable) >> EPIC_TBASE_SHIFT); chipset.sgmap = sgmap_map_create(APECS_SGMAP_BASE, APECS_SGMAP_BASE + APECS_SGMAP_SIZE, apecs_sgmap_map, sgtable); chipset.pci_sgmap = NULL; chipset.dmsize = 1UL * 1024UL * 1024UL * 1024UL; chipset.dmoffset = 1UL * 1024UL * 1024UL * 1024UL; } void apecs_init() { static int initted = 0; static struct swiz_space io_space, mem_space; if (initted) return; initted = 1; swiz_init_space(&io_space, KV(APECS_PCI_SIO)); swiz_init_space_hae(&mem_space, KV(APECS_PCI_SPARSE), apecs_set_hae_mem, 0); busspace_isa_io = (struct alpha_busspace *) &io_space; busspace_isa_mem = (struct alpha_busspace *) &mem_space; chipset = apecs_swiz_chipset; if (platform.pci_intr_init) platform.pci_intr_init(); } static int apecs_probe(device_t dev) { int memwidth; if (apecs0) return ENXIO; apecs0 = dev; memwidth = (REGVAL(COMANCHE_GCR) & COMANCHE_GCR_WIDEMEM) != 0 ? 128 : 64; if(memwidth == 64){ device_set_desc(dev, "DECchip 21071 Core Logic chipset"); } else { device_set_desc(dev, "DECchip 21072 Core Logic chipset"); } apecs_hae_mem = REGVAL(EPIC_HAXR1); isa_init_intr(); apecs_init_sgmap(); device_add_child(dev, "pcib", 0); return 0; } static int apecs_attach(device_t dev) { struct apecs_softc* sc = APECS_SOFTC(dev); apecs_init(); sc->dmem_base = APECS_PCI_DENSE; sc->smem_base = APECS_PCI_SPARSE; sc->io_base = APECS_PCI_SIO; sc->cfg0_base = KV(APECS_PCI_CONF); sc->cfg1_base = 0; set_iointr(alpha_dispatch_intr); snprintf(chipset_type, sizeof(chipset_type), "apecs"); chipset_bwx = 0; chipset_ports = APECS_PCI_SIO; chipset_memory = APECS_PCI_SPARSE; chipset_dense = APECS_PCI_DENSE; chipset_hae_mask = EPIC_HAXR1_EADDR; bus_generic_attach(dev); return 0; } static void apecs_disable_intr(uintptr_t vector) { int irq; irq = (vector - 0x900) >> 4; mtx_lock_spin(&icu_lock); platform.pci_intr_disable(irq); mtx_unlock_spin(&icu_lock); } static void apecs_enable_intr(uintptr_t vector) { int irq; irq = (vector - 0x900) >> 4; mtx_lock_spin(&icu_lock); platform.pci_intr_enable(irq); mtx_unlock_spin(&icu_lock); } static int apecs_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep) { int error, start; start = rman_get_start(irq); /* * the avanti routes interrupts through the isa interrupt * controller, so we need to special case it */ if(hwrpb->rpb_type == ST_DEC_2100_A50) return isa_setup_intr(dev, child, irq, flags, intr, arg, cookiep); error = rman_activate_resource(irq); if (error) return error; error = alpha_setup_intr(device_get_nameunit(child ? child : dev), 0x900 + (start << 4), intr, arg, flags, cookiep, &intrcnt[INTRCNT_EB64PLUS_IRQ + start], apecs_disable_intr, apecs_enable_intr); if (error) return error; /* Enable PCI interrupt */ mtx_lock_spin(&icu_lock); platform.pci_intr_enable(start); mtx_unlock_spin(&icu_lock); device_printf(child, "interrupting at APECS irq %d\n", start); return 0; } static int apecs_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { /* * the avanti routes interrupts through the isa interrupt * controller, so we need to special case it */ if(hwrpb->rpb_type == ST_DEC_2100_A50) return isa_teardown_intr(dev, child, irq, cookie); alpha_teardown_intr(cookie); return rman_deactivate_resource(irq); } DRIVER_MODULE(apecs, root, apecs_driver, apecs_devclass, 0, 0);