isa.c revision 93067
186232Stmm/*- 286232Stmm * Copyright (c) 1998 Doug Rabson 386232Stmm * Copyright (c) 2001 Thomas Moestl <tmm@FreeBSD.org> 486232Stmm * All rights reserved. 586232Stmm * 686232Stmm * Redistribution and use in source and binary forms, with or without 786232Stmm * modification, are permitted provided that the following conditions 886232Stmm * are met: 986232Stmm * 1. Redistributions of source code must retain the above copyright 1086232Stmm * notice, this list of conditions and the following disclaimer. 1186232Stmm * 2. Redistributions in binary form must reproduce the above copyright 1286232Stmm * notice, this list of conditions and the following disclaimer in the 1386232Stmm * documentation and/or other materials provided with the distribution. 1486232Stmm * 1586232Stmm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1686232Stmm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1786232Stmm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1886232Stmm * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1986232Stmm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2086232Stmm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2186232Stmm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2286232Stmm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2386232Stmm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2486232Stmm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2586232Stmm * SUCH DAMAGE. 2686232Stmm * 2786232Stmm * from: FreeBSD: src/sys/alpha/isa/isa.c,v 1.26 2001/07/11 2886232Stmm * 2986232Stmm * $FreeBSD: head/sys/sparc64/isa/isa.c 93067 2002-03-24 02:11:06Z tmm $ 3086232Stmm */ 3186232Stmm 3286232Stmm#include <sys/param.h> 3386232Stmm#include <sys/systm.h> 3486232Stmm#include <sys/kernel.h> 3586232Stmm#include <sys/module.h> 3686232Stmm#include <sys/bus.h> 3786232Stmm#include <machine/bus.h> 3886232Stmm#include <sys/malloc.h> 3986232Stmm#include <sys/proc.h> 4086232Stmm#include <sys/rman.h> 4186232Stmm#include <sys/interrupt.h> 4286232Stmm 4386232Stmm#include <isa/isareg.h> 4486232Stmm#include <isa/isavar.h> 4586232Stmm#include <isa/isa_common.h> 4686232Stmm 4786232Stmm#include <pci/pcireg.h> 4886232Stmm#include <pci/pcivar.h> 4986232Stmm 5086232Stmm#include <ofw/ofw_pci.h> 5186232Stmm#include <ofw/openfirm.h> 5286232Stmm 5386232Stmm#include <machine/intr_machdep.h> 5493067Stmm#include <machine/ofw_bus.h> 5586232Stmm#include <machine/resource.h> 5686232Stmm 5786232Stmm#include <sparc64/pci/ofw_pci.h> 5886232Stmm#include <sparc64/isa/ofw_isa.h> 5986232Stmm 6086232Stmm#include "sparcbus_if.h" 6186232Stmm 6286232Stmm/* There can be only one ISA bus, so it is safe to use globals. */ 6386232Stmmbus_space_tag_t isa_io_bt = NULL; 6486232Stmmbus_space_handle_t isa_io_hdl; 6586232Stmmbus_space_tag_t isa_mem_bt = NULL; 6686232Stmmbus_space_handle_t isa_mem_hdl; 6786232Stmm 6886232Stmmu_int64_t isa_io_base; 6986232Stmmu_int64_t isa_io_limit; 7086232Stmmu_int64_t isa_mem_base; 7186232Stmmu_int64_t isa_mem_limit; 7286232Stmm 7386232Stmmdevice_t isa_bus_device; 7486232Stmm 7586232Stmmstatic phandle_t isab_node; 7693067Stmmstatic u_int32_t isa_ino[8]; 7786232Stmm 7886232Stmm/* 7986232Stmm * XXX: This is really partly partly PCI-specific, but unfortunately is 8086232Stmm * differently enough to have to duplicate it here... 8186232Stmm */ 8286232Stmm#define ISAB_RANGE_PHYS(r) \ 8386232Stmm (((u_int64_t)(r)->phys_mid << 32) | (u_int64_t)(r)->phys_lo) 8486232Stmm#define ISAB_RANGE_SPACE(r) (((r)->phys_hi >> 24) & 0x03) 8586232Stmm 8686232Stmm#define ISAR_SPACE_IO 0x01 8786232Stmm#define ISAR_SPACE_MEM 0x02 8886232Stmm 8986232Stmm#define INRANGE(x, start, end) ((x) >= (start) && (x) <= (end)) 9086232Stmm 9186232Stmmstatic int isa_route_intr_res(device_t, u_long, u_long); 9286232Stmm 9386232Stmmintrmask_t 9486232Stmmisa_irq_pending(void) 9586232Stmm{ 9686232Stmm intrmask_t pending; 9786232Stmm int i; 9886232Stmm 9986232Stmm /* XXX: Is this correct? */ 10086232Stmm for (i = 7, pending = 0; i >= 0; i--) { 10186232Stmm pending <<= 1; 10293067Stmm if (isa_ino[i] != ORIR_NOTFOUND) { 10386232Stmm pending |= (SPARCBUS_INTR_PENDING(isa_bus_device, 10486232Stmm isa_ino[i]) == 0) ? 0 : 1; 10586232Stmm } 10686232Stmm } 10786232Stmm return (pending); 10886232Stmm} 10986232Stmm 11086232Stmmvoid 11186232Stmmisa_init(device_t dev) 11286232Stmm{ 11386232Stmm device_t bridge; 11486232Stmm phandle_t node; 11593067Stmm u_int32_t ino; 11686232Stmm struct isa_ranges *br; 11786232Stmm int nbr, i; 11886232Stmm 11986232Stmm /* The parent of the bus must be a PCI-ISA bridge. */ 12086232Stmm bridge = device_get_parent(dev); 12193067Stmm isab_node = ofw_pci_node(bridge); 12286232Stmm nbr = OF_getprop_alloc(isab_node, "ranges", sizeof(*br), (void **)&br); 12386232Stmm if (nbr <= 0) 12486232Stmm panic("isa_init: cannot get bridge range property"); 12593067Stmm /* 12693067Stmm * This is really a bad kluge; however, it is needed to provide 12793067Stmm * isa_irq_pending(). 12893067Stmm */ 12993067Stmm for (i = 0; i < 8; i++) 13093067Stmm isa_ino[i] = ORIR_NOTFOUND; 13193067Stmm for (node = OF_child(isab_node); node != 0; node = OF_peer(node)) { 13293067Stmm if (OF_getprop(node, "interrupts", &ino, sizeof(ino)) == -1) 13393067Stmm continue; 13493067Stmm if (ino > 7) 13593067Stmm panic("isa_init: XXX: ino too large"); 13693067Stmm isa_ino[ino] = ofw_bus_route_intr(node, ino); 13786232Stmm } 13886232Stmm 13986232Stmm for (nbr -= 1; nbr >= 0; nbr--) { 14086232Stmm switch(ISAB_RANGE_SPACE(br + nbr)) { 14186232Stmm case ISAR_SPACE_IO: 14286232Stmm /* This is probably always 0. */ 14386232Stmm isa_io_base = ISAB_RANGE_PHYS(&br[nbr]); 14486232Stmm isa_io_limit = br[nbr].size; 14586232Stmm isa_io_hdl = SPARCBUS_GET_BUS_HANDLE(bridge, SBBT_IO, 14686232Stmm isa_io_base, &isa_io_bt); 14786232Stmm break; 14886232Stmm case ISAR_SPACE_MEM: 14986232Stmm /* This is probably always 0. */ 15086232Stmm isa_mem_base = ISAB_RANGE_PHYS(&br[nbr]); 15186232Stmm isa_mem_limit = br[nbr].size; 15286232Stmm isa_mem_hdl = SPARCBUS_GET_BUS_HANDLE(bridge, SBBT_MEM, 15386232Stmm isa_mem_base, &isa_mem_bt); 15486232Stmm break; 15586232Stmm } 15686232Stmm } 15786232Stmm free(br, M_OFWPROP); 15886232Stmm} 15986232Stmm 16086232Stmmstatic int 16186232Stmmisa_route_intr_res(device_t bus, u_long start, u_long end) 16286232Stmm{ 16386232Stmm int res; 16486232Stmm 16586232Stmm if (start != end) { 16686232Stmm panic("isa_route_intr_res: allocation of interrupt range not " 16786232Stmm "supported (0x%lx - 0x%lx)", start, end); 16886232Stmm } 16993067Stmm if (start > 7) 17093067Stmm panic("isa_route_intr_res: start out of isa range"); 17193067Stmm res = isa_ino[start]; 17286232Stmm if (res == 255) 17386232Stmm device_printf(bus, "could not map interrupt %d\n", res); 17486232Stmm return (res); 17586232Stmm} 17686232Stmm 17786232Stmmstruct resource * 17886232Stmmisa_alloc_resource(device_t bus, device_t child, int type, int *rid, 17986232Stmm u_long start, u_long end, u_long count, u_int flags) 18086232Stmm{ 18186232Stmm /* 18286232Stmm * Consider adding a resource definition. We allow rid 0-1 for 18386232Stmm * irq and drq, 0-3 for memory and 0-7 for ports which is 18486232Stmm * sufficient for isapnp. 18586232Stmm */ 18686232Stmm int passthrough = (device_get_parent(child) != bus); 18786232Stmm int isdefault = (start == 0UL && end == ~0UL); 18886232Stmm struct isa_device* idev = DEVTOISA(child); 18986232Stmm struct resource_list *rl = &idev->id_resources; 19086232Stmm struct resource_list_entry *rle; 19186232Stmm u_long base, limit; 19286232Stmm 19386232Stmm if (!passthrough && !isdefault) { 19486232Stmm rle = resource_list_find(rl, type, *rid); 19586232Stmm if (!rle) { 19686232Stmm if (*rid < 0) 19786232Stmm return 0; 19886232Stmm switch (type) { 19986232Stmm case SYS_RES_IRQ: 20086232Stmm if (*rid >= ISA_NIRQ) 20186232Stmm return 0; 20286232Stmm break; 20386232Stmm case SYS_RES_DRQ: 20486232Stmm if (*rid >= ISA_NDRQ) 20586232Stmm return 0; 20686232Stmm break; 20786232Stmm case SYS_RES_MEMORY: 20886232Stmm if (*rid >= ISA_NMEM) 20986232Stmm return 0; 21086232Stmm break; 21186232Stmm case SYS_RES_IOPORT: 21286232Stmm if (*rid >= ISA_NPORT) 21386232Stmm return 0; 21486232Stmm break; 21586232Stmm default: 21686232Stmm return 0; 21786232Stmm } 21886232Stmm resource_list_add(rl, type, *rid, start, end, count); 21986232Stmm } 22086232Stmm } 22186232Stmm 22286232Stmm /* 22386232Stmm * Add the base, change default allocations to be between base and 22486232Stmm * limit, and reject allocations if a resource type is not enabled. 22586232Stmm */ 22686232Stmm base = limit = 0; 22786232Stmm switch(type) { 22886232Stmm case SYS_RES_MEMORY: 22986232Stmm if (isa_mem_bt == NULL) 23086232Stmm return (NULL); 23186232Stmm base = isa_mem_base; 23286232Stmm limit = base + isa_mem_limit; 23386232Stmm break; 23486232Stmm case SYS_RES_IOPORT: 23586232Stmm if (isa_io_bt == NULL) 23686232Stmm return (NULL); 23786232Stmm base = isa_io_base; 23886232Stmm limit = base + isa_io_limit; 23986232Stmm break; 24086232Stmm case SYS_RES_IRQ: 24186232Stmm if (isdefault && passthrough) 24286232Stmm panic("isa_alloc_resource: cannot pass through default " 24386232Stmm "irq allocation"); 24486232Stmm if (!isdefault) { 24586232Stmm start = end = isa_route_intr_res(bus, start, end); 24686232Stmm if (start == 255) 24786232Stmm return (NULL); 24886232Stmm } 24986232Stmm break; 25086232Stmm default: 25186232Stmm panic("isa_alloc_resource: unsupported resource type %d", type); 25286232Stmm } 25386232Stmm if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { 25486232Stmm start = ulmin(start + base, limit); 25586232Stmm end = ulmin(end + base, limit); 25686232Stmm } 25786232Stmm 25886232Stmm /* 25986232Stmm * This inlines a modified resource_list_alloc(); this is needed 26090612Stmm * because the resources need to have offsets added to them, which 26190612Stmm * cannot be done beforehand without patching the resource list entries 26286232Stmm * (which is ugly). 26386232Stmm */ 26486232Stmm if (passthrough) { 26586232Stmm return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child, 26686232Stmm type, rid, start, end, count, flags)); 26786232Stmm } 26886232Stmm 26986232Stmm rle = resource_list_find(rl, type, *rid); 27086232Stmm if (rle == NULL) 27186232Stmm return (NULL); /* no resource of that type/rid */ 27286232Stmm 27386232Stmm if (rle->res != NULL) 27486232Stmm panic("isa_alloc_resource: resource entry is busy"); 27586232Stmm 27686232Stmm if (isdefault) { 27786232Stmm start = rle->start; 27886232Stmm count = ulmax(count, rle->count); 27986232Stmm end = ulmax(rle->end, start + count - 1); 28086232Stmm switch (type) { 28186232Stmm case SYS_RES_MEMORY: 28286232Stmm case SYS_RES_IOPORT: 28386232Stmm start += base; 28486232Stmm end += base; 28586232Stmm if (!INRANGE(start, base, limit) || 28686232Stmm !INRANGE(end, base, limit)) { 28786232Stmm panic("isa_alloc_resource: resource list entry " 28886232Stmm "out of bus range (0x%lx - 0x%lx not in " 28986232Stmm "0x%lx - 0x%lx)", start, end, base, limit); 29086232Stmm } 29186232Stmm break; 29286232Stmm case SYS_RES_IRQ: 29386232Stmm start = end = isa_route_intr_res(bus, start, end); 29486232Stmm if (start == 255) 29586232Stmm return (NULL); 29686232Stmm break; 29786232Stmm } 29886232Stmm } 29986232Stmm 30086232Stmm rle->res = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, 30186232Stmm type, rid, start, end, count, flags); 30286232Stmm 30386232Stmm /* 30486232Stmm * Record the new range. 30586232Stmm */ 30686232Stmm if (rle->res != NULL) { 30786232Stmm rle->start = rman_get_start(rle->res) - base; 30886232Stmm rle->end = rman_get_end(rle->res) - base; 30986232Stmm rle->count = count; 31086232Stmm } 31186232Stmm 31286232Stmm return (rle->res); 31386232Stmm} 31486232Stmm 31586232Stmmint 31686232Stmmisa_release_resource(device_t bus, device_t child, int type, int rid, 31786232Stmm struct resource *res) 31886232Stmm{ 31986232Stmm struct isa_device* idev = DEVTOISA(child); 32086232Stmm struct resource_list *rl = &idev->id_resources; 32186232Stmm 32286232Stmm return (resource_list_release(rl, bus, child, type, rid, res)); 32386232Stmm} 32486232Stmm 32586232Stmmint 32686232Stmmisa_setup_intr(device_t dev, device_t child, 32786232Stmm struct resource *irq, int flags, 32886232Stmm driver_intr_t *intr, void *arg, void **cookiep) 32986232Stmm{ 33086232Stmm 33186232Stmm /* 33286232Stmm * Just pass through. This is going to be handled by either one of 33386232Stmm * the parent PCI buses or the nexus device. 33486232Stmm * The interrupt was routed at allocation time. 33586232Stmm */ 33686232Stmm return (BUS_SETUP_INTR(device_get_parent(dev), child, irq, flags, intr, 33786232Stmm arg, cookiep)); 33886232Stmm} 33986232Stmm 34086232Stmmint 34186232Stmmisa_teardown_intr(device_t dev, device_t child, 34286232Stmm struct resource *irq, void *cookie) 34386232Stmm{ 34486232Stmm 34586232Stmm return (BUS_TEARDOWN_INTR(device_get_parent(dev), child, irq, cookie)); 34686232Stmm} 347