From ebaf8798e97bd87eccad436a6b68f3760cd72f24 Mon Sep 17 00:00:00 2001 From: Thomas Moestl Date: Fri, 9 Nov 2001 20:21:21 +0000 Subject: [PATCH] Add ISA support code for sparc64. --- sys/sparc64/isa/isa.c | 347 ++++++++++++++++++++++++++++++++++++++ sys/sparc64/isa/ofw_isa.c | 100 +++++++++++ sys/sparc64/isa/ofw_isa.h | 83 +++++++++ 3 files changed, 530 insertions(+) create mode 100644 sys/sparc64/isa/isa.c create mode 100644 sys/sparc64/isa/ofw_isa.c create mode 100644 sys/sparc64/isa/ofw_isa.h diff --git a/sys/sparc64/isa/isa.c b/sys/sparc64/isa/isa.c new file mode 100644 index 000000000000..8183ae104597 --- /dev/null +++ b/sys/sparc64/isa/isa.c @@ -0,0 +1,347 @@ +/*- + * Copyright (c) 1998 Doug Rabson + * Copyright (c) 2001 Thomas Moestl + * 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. + * + * from: FreeBSD: src/sys/alpha/isa/isa.c,v 1.26 2001/07/11 + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include "sparcbus_if.h" + +/* There can be only one ISA bus, so it is safe to use globals. */ +bus_space_tag_t isa_io_bt = NULL; +bus_space_handle_t isa_io_hdl; +bus_space_tag_t isa_mem_bt = NULL; +bus_space_handle_t isa_mem_hdl; + +u_int64_t isa_io_base; +u_int64_t isa_io_limit; +u_int64_t isa_mem_base; +u_int64_t isa_mem_limit; + +device_t isa_bus_device; + +static struct ofw_pci_register isab_reg; +static struct ofw_pci_imap *isab_imap; +static int isab_nimap; +static struct ofw_pci_imap_msk isab_imap_msk; +static phandle_t isab_node; + +/* + * XXX: This is really partly partly PCI-specific, but unfortunately is + * differently enough to have to duplicate it here... + */ +#define ISAB_RANGE_PHYS(r) \ + (((u_int64_t)(r)->phys_mid << 32) | (u_int64_t)(r)->phys_lo) +#define ISAB_RANGE_SPACE(r) (((r)->phys_hi >> 24) & 0x03) + +#define ISAR_SPACE_IO 0x01 +#define ISAR_SPACE_MEM 0x02 + +#define INRANGE(x, start, end) ((x) >= (start) && (x) <= (end)) + +static int isa_route_intr_res(device_t, u_long, u_long); + +static int isa_ino[8]; + +intrmask_t +isa_irq_pending(void) +{ + intrmask_t pending; + int i; + + /* XXX: Is this correct? */ + for (i = 7, pending = 0; i >= 0; i--) { + pending <<= 1; + if (isa_ino[i] != 255) { + pending |= (SPARCBUS_INTR_PENDING(isa_bus_device, + isa_ino[i]) == 0) ? 0 : 1; + } + } + return (pending); +} + +void +isa_init(device_t dev) +{ + device_t bridge; + phandle_t node; + struct isa_ranges *br; + int nbr, i; + + /* The parent of the bus must be a PCI-ISA bridge. */ + bridge = device_get_parent(dev); + isab_node = ofw_pci_find_node(pci_get_bus(bridge), pci_get_slot(bridge), + pci_get_function(bridge)); + if (OF_getprop(isab_node, "reg", &isab_reg, sizeof(isab_reg)) < 0) + panic("isa_init: cannot get bridge reg property"); + nbr = OF_getprop_alloc(isab_node, "ranges", sizeof(*br), (void **)&br); + if (nbr <= 0) + panic("isa_init: cannot get bridge range property"); + node = isab_node; + isab_nimap = ofw_pci_find_imap(node, &isab_imap, &isab_imap_msk); + if (isab_nimap == -1) + panic("isa_init: could not find interrupt-map"); + for (i = 0; i < 8; i++) { + isa_ino[i] = ofw_pci_route_intr2(i, &isab_reg, isab_imap, + isab_nimap, &isab_imap_msk); + } + + + for (nbr -= 1; nbr >= 0; nbr--) { + switch(ISAB_RANGE_SPACE(br + nbr)) { + case ISAR_SPACE_IO: + /* This is probably always 0. */ + isa_io_base = ISAB_RANGE_PHYS(&br[nbr]); + isa_io_limit = br[nbr].size; + isa_io_hdl = SPARCBUS_GET_BUS_HANDLE(bridge, SBBT_IO, + isa_io_base, &isa_io_bt); + break; + case ISAR_SPACE_MEM: + /* This is probably always 0. */ + isa_mem_base = ISAB_RANGE_PHYS(&br[nbr]); + isa_mem_limit = br[nbr].size; + isa_mem_hdl = SPARCBUS_GET_BUS_HANDLE(bridge, SBBT_MEM, + isa_mem_base, &isa_mem_bt); + break; + } + } + free(br, M_OFWPROP); +} + +static int +isa_route_intr_res(device_t bus, u_long start, u_long end) +{ + int res; + + if (start != end) { + panic("isa_route_intr_res: allocation of interrupt range not " + "supported (0x%lx - 0x%lx)", start, end); + } + res = ofw_pci_route_intr2(start, &isab_reg, isab_imap, isab_nimap, + &isab_imap_msk); + if (res == 255) + device_printf(bus, "could not map interrupt %d\n", res); + return (res); +} + +struct resource * +isa_alloc_resource(device_t bus, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + /* + * Consider adding a resource definition. We allow rid 0-1 for + * irq and drq, 0-3 for memory and 0-7 for ports which is + * sufficient for isapnp. + */ + int passthrough = (device_get_parent(child) != bus); + int isdefault = (start == 0UL && end == ~0UL); + struct isa_device* idev = DEVTOISA(child); + struct resource_list *rl = &idev->id_resources; + struct resource_list_entry *rle; + u_long base, limit; + + if (!passthrough && !isdefault) { + rle = resource_list_find(rl, type, *rid); + if (!rle) { + if (*rid < 0) + return 0; + switch (type) { + case SYS_RES_IRQ: + if (*rid >= ISA_NIRQ) + return 0; + break; + case SYS_RES_DRQ: + if (*rid >= ISA_NDRQ) + return 0; + break; + case SYS_RES_MEMORY: + if (*rid >= ISA_NMEM) + return 0; + break; + case SYS_RES_IOPORT: + if (*rid >= ISA_NPORT) + return 0; + break; + default: + return 0; + } + resource_list_add(rl, type, *rid, start, end, count); + } + } + + /* + * Add the base, change default allocations to be between base and + * limit, and reject allocations if a resource type is not enabled. + */ + base = limit = 0; + switch(type) { + case SYS_RES_MEMORY: + if (isa_mem_bt == NULL) + return (NULL); + base = isa_mem_base; + limit = base + isa_mem_limit; + break; + case SYS_RES_IOPORT: + if (isa_io_bt == NULL) + return (NULL); + base = isa_io_base; + limit = base + isa_io_limit; + break; + case SYS_RES_IRQ: + if (isdefault && passthrough) + panic("isa_alloc_resource: cannot pass through default " + "irq allocation"); + if (!isdefault) { + start = end = isa_route_intr_res(bus, start, end); + if (start == 255) + return (NULL); + } + break; + default: + panic("isa_alloc_resource: unsupported resource type %d", type); + } + if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { + start = ulmin(start + base, limit); + end = ulmin(end + base, limit); + } + + /* + * This inlines a modified resource_list_alloc(); this is needed + * because the resources need to have offsets added to them, whcih + * cannot be done beforehand without patching the resource list entires + * (which is ugly). + */ + if (passthrough) { + return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child, + type, rid, start, end, count, flags)); + } + + rle = resource_list_find(rl, type, *rid); + if (rle == NULL) + return (NULL); /* no resource of that type/rid */ + + if (rle->res != NULL) + panic("isa_alloc_resource: resource entry is busy"); + + if (isdefault) { + start = rle->start; + count = ulmax(count, rle->count); + end = ulmax(rle->end, start + count - 1); + switch (type) { + case SYS_RES_MEMORY: + case SYS_RES_IOPORT: + start += base; + end += base; + if (!INRANGE(start, base, limit) || + !INRANGE(end, base, limit)) { + panic("isa_alloc_resource: resource list entry " + "out of bus range (0x%lx - 0x%lx not in " + "0x%lx - 0x%lx)", start, end, base, limit); + } + break; + case SYS_RES_IRQ: + start = end = isa_route_intr_res(bus, start, end); + if (start == 255) + return (NULL); + break; + } + } + + rle->res = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, + type, rid, start, end, count, flags); + + /* + * Record the new range. + */ + if (rle->res != NULL) { + rle->start = rman_get_start(rle->res) - base; + rle->end = rman_get_end(rle->res) - base; + rle->count = count; + } + + return (rle->res); +} + +int +isa_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *res) +{ + struct isa_device* idev = DEVTOISA(child); + struct resource_list *rl = &idev->id_resources; + + return (resource_list_release(rl, bus, child, type, rid, res)); +} + +int +isa_setup_intr(device_t dev, device_t child, + struct resource *irq, int flags, + driver_intr_t *intr, void *arg, void **cookiep) +{ + + /* + * Just pass through. This is going to be handled by either one of + * the parent PCI buses or the nexus device. + * The interrupt was routed at allocation time. + */ + return (BUS_SETUP_INTR(device_get_parent(dev), child, irq, flags, intr, + arg, cookiep)); +} + +int +isa_teardown_intr(device_t dev, device_t child, + struct resource *irq, void *cookie) +{ + + return (BUS_TEARDOWN_INTR(device_get_parent(dev), child, irq, cookie)); +} diff --git a/sys/sparc64/isa/ofw_isa.c b/sys/sparc64/isa/ofw_isa.c new file mode 100644 index 000000000000..4dc65f1e8910 --- /dev/null +++ b/sys/sparc64/isa/ofw_isa.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 1999, 2000 Matthew R. Green + * Copyright (c) 2001 Thomas Moestl + * 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. + * 3. 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. + * + * from: NetBSD: ebus.c,v 1.26 2001/09/10 16:27:53 eeh Exp + * + * $FreeBSD$ + */ + +/* + * Helper functions which can be used in both ISA and EBus code. + */ + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +/* + * This applies only for an ISA/EBus with an own interrupt-map property. + */ +int +ofw_isa_map_intr(struct isa_imap *imap, int nimap, struct isa_imap_msk *imapmsk, + int intr, struct isa_regs *regs, int nregs) +{ + char regm[8]; + + return (ofw_bus_route_intr(intr, regs, sizeof(*regs), 8, nregs, + imap, nimap, imapmsk, regm)); +} + +/* XXX: this only supports PCI as parent bus right now. */ +int +ofw_isa_map_iorange(struct isa_ranges *range, int nrange, u_long *start, + u_long *end) +{ + u_int64_t offs, cstart, cend; + int i; + + for (i = 0; i < nrange; i++) { + cstart = ((u_int64_t)range[i].child_hi << 32) | + range[i].child_lo; + cend = cstart + range[i].size; + if (*start < cstart || *start > cend) + continue; + if (*end < cstart || *end > cend) { + panic("ofw_isa_map_iorange: iorange crosses pci " + "ranges (%#lx not in %#lx - %#lx)", *end, cstart, + cend); + } + offs = (((u_int64_t)range[i].phys_mid << 32) | + range[i].phys_lo); + *start = *start + offs - cstart; + *end = *end + offs - cstart; + /* Isolate address space and find the right tag */ + switch (ISA_RANGE_PS(&range[i])) { + case PCI_CS_IO: + return (SYS_RES_IOPORT); + case PCI_CS_MEM32: + return (SYS_RES_MEMORY); + default: + panic("ofw_isa_map_iorange: illegal space %x", + ISA_RANGE_PS(&range[i])); + break; + } + } + panic("ofw_isa_map_iorange: could not map range %#lx - %#lx", + *start, *end); +} diff --git a/sys/sparc64/isa/ofw_isa.h b/sys/sparc64/isa/ofw_isa.h new file mode 100644 index 000000000000..9402657abb0f --- /dev/null +++ b/sys/sparc64/isa/ofw_isa.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 1999, 2000 Matthew R. Green + * Copyright (c) 2001 Thomas Moestl + * 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. + * 3. 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. + * + * from: NetBSD: ebus.c,v 1.26 2001/09/10 16:27:53 eeh Exp + * + * $FreeBSD$ + */ + +#ifndef _SPARC64_ISA_OFW_ISA_H_ +#define _SPARC64_ISA_OFW_ISA_H_ + +/* + * ISA PROM structures + */ +struct isa_regs { + u_int32_t phys_hi; /* high bits of physaddr */ + u_int32_t phys_lo; + u_int32_t size; +}; + +#define ISA_REG_PHYS(r) \ + ((((u_int64_t)((r)->phys_hi)) << 32) | ((u_int64_t)(r)->phys_lo)) + +/* XXX: this is a guess. Verify... */ +struct isa_ranges { + u_int32_t child_hi; + u_int32_t child_lo; + u_int32_t phys_hi; + u_int32_t phys_mid; + u_int32_t phys_lo; + u_int32_t size; +}; + +#define ISA_RANGE_CHILD(r) \ + ((((u_int64_t)((r)->child_hi)) << 32) | ((u_int64_t)(r)->child_lo)) +#define ISA_RANGE_PS(r) (((r)->phys_hi >> 24) & 0x03) + +struct isa_imap { + u_int32_t phys_hi; /* high phys addr mask */ + u_int32_t phys_lo; /* low phys addr mask */ + u_int32_t intr; /* interrupt mask */ + int32_t cnode; /* child node */ + u_int32_t cintr; /* child interrupt */ +}; + +struct isa_imap_msk { + u_int32_t phys_hi; /* high phys addr */ + u_int32_t phys_lo; /* low phys addr */ + u_int32_t intr; /* interrupt */ +}; + +/* Map an interrupt property to an INO */ +int ofw_isa_map_intr(struct isa_imap *, int, struct isa_imap_msk *, int, + struct isa_regs *, int); +/* Map an IO range. Returns the resource type of the range. */ +int ofw_isa_map_iorange(struct isa_ranges *, int, u_long *, u_long *); + +#endif /* !_SPARC64_ISA_OFW_ISA_H_ */