isa.c revision 119338
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 119338 2003-08-23 00:11:16Z imp $ 3086232Stmm */ 3186232Stmm 32117119Stmm#include "opt_ofw_pci.h" 33117119Stmm 3486232Stmm#include <sys/param.h> 3586232Stmm#include <sys/systm.h> 3686232Stmm#include <sys/kernel.h> 3786232Stmm#include <sys/module.h> 3886232Stmm#include <sys/bus.h> 3986232Stmm#include <machine/bus.h> 4086232Stmm#include <sys/malloc.h> 4186232Stmm#include <sys/proc.h> 4286232Stmm#include <sys/rman.h> 4386232Stmm#include <sys/interrupt.h> 4486232Stmm 4586232Stmm#include <isa/isareg.h> 4686232Stmm#include <isa/isavar.h> 4786232Stmm#include <isa/isa_common.h> 4886232Stmm 49119291Simp#include <dev/pci/pcireg.h> 50119291Simp#include <dev/pci/pcivar.h> 5186232Stmm 52119338Simp#include <dev/ofw/ofw_pci.h> 53119338Simp#include <dev/ofw/openfirm.h> 5486232Stmm 5586232Stmm#include <machine/intr_machdep.h> 5693067Stmm#include <machine/ofw_bus.h> 5786232Stmm#include <machine/resource.h> 5886232Stmm 5986232Stmm#include <sparc64/pci/ofw_pci.h> 6086232Stmm#include <sparc64/isa/ofw_isa.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; 76117119Stmmstatic ofw_pci_intr_t isa_ino[8]; 7786232Stmm 78117119Stmm#ifdef OFW_NEWPCI 79117119Stmmstruct ofw_bus_iinfo isa_iinfo; 80117119Stmm#endif 81117119Stmm 8286232Stmm/* 8386232Stmm * XXX: This is really partly partly PCI-specific, but unfortunately is 8486232Stmm * differently enough to have to duplicate it here... 8586232Stmm */ 8686232Stmm#define ISAB_RANGE_PHYS(r) \ 8786232Stmm (((u_int64_t)(r)->phys_mid << 32) | (u_int64_t)(r)->phys_lo) 8886232Stmm#define ISAB_RANGE_SPACE(r) (((r)->phys_hi >> 24) & 0x03) 8986232Stmm 9086232Stmm#define ISAR_SPACE_IO 0x01 9186232Stmm#define ISAR_SPACE_MEM 0x02 9286232Stmm 9386232Stmm#define INRANGE(x, start, end) ((x) >= (start) && (x) <= (end)) 9486232Stmm 9586232Stmmstatic int isa_route_intr_res(device_t, u_long, u_long); 9686232Stmm 9786232Stmmintrmask_t 9886232Stmmisa_irq_pending(void) 9986232Stmm{ 10086232Stmm intrmask_t pending; 10186232Stmm int i; 10286232Stmm 10386232Stmm /* XXX: Is this correct? */ 10486232Stmm for (i = 7, pending = 0; i >= 0; i--) { 105117119Stmm pending <<= 1; 106117119Stmm if (isa_ino[i] != PCI_INVALID_IRQ) { 107117119Stmm pending |= (OFW_PCI_INTR_PENDING(isa_bus_device, 10886232Stmm isa_ino[i]) == 0) ? 0 : 1; 10986232Stmm } 11086232Stmm } 11186232Stmm return (pending); 11286232Stmm} 11386232Stmm 11486232Stmmvoid 11586232Stmmisa_init(device_t dev) 11686232Stmm{ 11786232Stmm device_t bridge; 11886232Stmm phandle_t node; 119117119Stmm ofw_isa_intr_t ino; 120117119Stmm#ifndef OFW_NEWPCI 121117119Stmm ofw_pci_intr_t rino; 122117119Stmm#endif 12386232Stmm struct isa_ranges *br; 12486232Stmm int nbr, i; 12586232Stmm 12686232Stmm /* The parent of the bus must be a PCI-ISA bridge. */ 12786232Stmm bridge = device_get_parent(dev); 128117119Stmm#ifdef OFW_NEWPCI 129117119Stmm isab_node = ofw_pci_get_node(bridge); 130117119Stmm#else 13193067Stmm isab_node = ofw_pci_node(bridge); 132117119Stmm#endif 13386232Stmm nbr = OF_getprop_alloc(isab_node, "ranges", sizeof(*br), (void **)&br); 13486232Stmm if (nbr <= 0) 13586232Stmm panic("isa_init: cannot get bridge range property"); 136117119Stmm 137117119Stmm#ifdef OFW_NEWPCI 138117119Stmm ofw_bus_setup_iinfo(isab_node, &isa_iinfo, sizeof(ofw_isa_intr_t)); 139117119Stmm#endif 140117119Stmm 14193067Stmm /* 142117119Stmm * This is really a bad kludge; however, it is needed to provide 143117119Stmm * isa_irq_pending(), which is unfortunately still used by some 144117119Stmm * drivers. 14593067Stmm */ 14693067Stmm for (i = 0; i < 8; i++) 147117119Stmm isa_ino[i] = PCI_INVALID_IRQ; 14893067Stmm for (node = OF_child(isab_node); node != 0; node = OF_peer(node)) { 14993067Stmm if (OF_getprop(node, "interrupts", &ino, sizeof(ino)) == -1) 15093067Stmm continue; 15193067Stmm if (ino > 7) 15293067Stmm panic("isa_init: XXX: ino too large"); 153117119Stmm#ifdef OFW_NEWPCI 154117119Stmm isa_ino[ino] = ofw_isa_route_intr(bridge, node, &isa_iinfo, 155117119Stmm ino); 156117119Stmm#else 157117119Stmm rino = ofw_bus_route_intr(node, ino, ofw_pci_orb_callback, dev); 158117119Stmm isa_ino[ino] = rino == ORIR_NOTFOUND ? PCI_INVALID_IRQ : rino; 159117119Stmm#endif 16086232Stmm } 16186232Stmm 16286232Stmm for (nbr -= 1; nbr >= 0; nbr--) { 16386232Stmm switch(ISAB_RANGE_SPACE(br + nbr)) { 16486232Stmm case ISAR_SPACE_IO: 16586232Stmm /* This is probably always 0. */ 16686232Stmm isa_io_base = ISAB_RANGE_PHYS(&br[nbr]); 16786232Stmm isa_io_limit = br[nbr].size; 168117119Stmm isa_io_hdl = OFW_PCI_GET_BUS_HANDLE(bridge, 169117119Stmm SYS_RES_IOPORT, isa_io_base, &isa_io_bt); 17086232Stmm break; 17186232Stmm case ISAR_SPACE_MEM: 17286232Stmm /* This is probably always 0. */ 17386232Stmm isa_mem_base = ISAB_RANGE_PHYS(&br[nbr]); 17486232Stmm isa_mem_limit = br[nbr].size; 175117119Stmm isa_mem_hdl = OFW_PCI_GET_BUS_HANDLE(bridge, 176117119Stmm SYS_RES_MEMORY, isa_mem_base, &isa_mem_bt); 17786232Stmm break; 17886232Stmm } 17986232Stmm } 18086232Stmm free(br, M_OFWPROP); 18186232Stmm} 18286232Stmm 18386232Stmmstatic int 18486232Stmmisa_route_intr_res(device_t bus, u_long start, u_long end) 18586232Stmm{ 18686232Stmm int res; 18786232Stmm 18886232Stmm if (start != end) { 18986232Stmm panic("isa_route_intr_res: allocation of interrupt range not " 19086232Stmm "supported (0x%lx - 0x%lx)", start, end); 19186232Stmm } 19293067Stmm if (start > 7) 19393067Stmm panic("isa_route_intr_res: start out of isa range"); 19493067Stmm res = isa_ino[start]; 195117119Stmm if (res == PCI_INVALID_IRQ) 19686232Stmm device_printf(bus, "could not map interrupt %d\n", res); 19786232Stmm return (res); 19886232Stmm} 19986232Stmm 20086232Stmmstruct resource * 20186232Stmmisa_alloc_resource(device_t bus, device_t child, int type, int *rid, 20286232Stmm u_long start, u_long end, u_long count, u_int flags) 20386232Stmm{ 20486232Stmm /* 20586232Stmm * Consider adding a resource definition. We allow rid 0-1 for 20686232Stmm * irq and drq, 0-3 for memory and 0-7 for ports which is 20786232Stmm * sufficient for isapnp. 20886232Stmm */ 20986232Stmm int passthrough = (device_get_parent(child) != bus); 21086232Stmm int isdefault = (start == 0UL && end == ~0UL); 21186232Stmm struct isa_device* idev = DEVTOISA(child); 21286232Stmm struct resource_list *rl = &idev->id_resources; 21386232Stmm struct resource_list_entry *rle; 21486232Stmm u_long base, limit; 21586232Stmm 21686232Stmm if (!passthrough && !isdefault) { 21786232Stmm rle = resource_list_find(rl, type, *rid); 21886232Stmm if (!rle) { 21986232Stmm if (*rid < 0) 22086232Stmm return 0; 22186232Stmm switch (type) { 22286232Stmm case SYS_RES_IRQ: 22386232Stmm if (*rid >= ISA_NIRQ) 22486232Stmm return 0; 22586232Stmm break; 22686232Stmm case SYS_RES_DRQ: 22786232Stmm if (*rid >= ISA_NDRQ) 22886232Stmm return 0; 22986232Stmm break; 23086232Stmm case SYS_RES_MEMORY: 23186232Stmm if (*rid >= ISA_NMEM) 23286232Stmm return 0; 23386232Stmm break; 23486232Stmm case SYS_RES_IOPORT: 23586232Stmm if (*rid >= ISA_NPORT) 23686232Stmm return 0; 23786232Stmm break; 23886232Stmm default: 23986232Stmm return 0; 24086232Stmm } 24186232Stmm resource_list_add(rl, type, *rid, start, end, count); 24286232Stmm } 24386232Stmm } 24486232Stmm 24586232Stmm /* 24686232Stmm * Add the base, change default allocations to be between base and 24786232Stmm * limit, and reject allocations if a resource type is not enabled. 24886232Stmm */ 24986232Stmm base = limit = 0; 25086232Stmm switch(type) { 25186232Stmm case SYS_RES_MEMORY: 25286232Stmm if (isa_mem_bt == NULL) 25386232Stmm return (NULL); 25486232Stmm base = isa_mem_base; 25586232Stmm limit = base + isa_mem_limit; 25686232Stmm break; 25786232Stmm case SYS_RES_IOPORT: 25886232Stmm if (isa_io_bt == NULL) 25986232Stmm return (NULL); 26086232Stmm base = isa_io_base; 26186232Stmm limit = base + isa_io_limit; 26286232Stmm break; 26386232Stmm case SYS_RES_IRQ: 26486232Stmm if (isdefault && passthrough) 26586232Stmm panic("isa_alloc_resource: cannot pass through default " 26686232Stmm "irq allocation"); 26786232Stmm if (!isdefault) { 26886232Stmm start = end = isa_route_intr_res(bus, start, end); 269117119Stmm if (start == PCI_INVALID_IRQ) 27086232Stmm return (NULL); 27186232Stmm } 27286232Stmm break; 27386232Stmm default: 27486232Stmm panic("isa_alloc_resource: unsupported resource type %d", type); 27586232Stmm } 27686232Stmm if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { 27786232Stmm start = ulmin(start + base, limit); 27886232Stmm end = ulmin(end + base, limit); 27986232Stmm } 280117119Stmm 28186232Stmm /* 28286232Stmm * This inlines a modified resource_list_alloc(); this is needed 28390612Stmm * because the resources need to have offsets added to them, which 28490612Stmm * cannot be done beforehand without patching the resource list entries 28586232Stmm * (which is ugly). 28686232Stmm */ 28786232Stmm if (passthrough) { 28886232Stmm return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child, 28986232Stmm type, rid, start, end, count, flags)); 29086232Stmm } 29186232Stmm 29286232Stmm rle = resource_list_find(rl, type, *rid); 29386232Stmm if (rle == NULL) 29486232Stmm return (NULL); /* no resource of that type/rid */ 29586232Stmm 29686232Stmm if (rle->res != NULL) 29786232Stmm panic("isa_alloc_resource: resource entry is busy"); 29886232Stmm 29986232Stmm if (isdefault) { 30086232Stmm start = rle->start; 30186232Stmm count = ulmax(count, rle->count); 30286232Stmm end = ulmax(rle->end, start + count - 1); 30386232Stmm switch (type) { 30486232Stmm case SYS_RES_MEMORY: 30586232Stmm case SYS_RES_IOPORT: 30686232Stmm start += base; 30786232Stmm end += base; 30886232Stmm if (!INRANGE(start, base, limit) || 30993681Stmm !INRANGE(end, base, limit)) 31093681Stmm return (NULL); 31186232Stmm break; 31286232Stmm case SYS_RES_IRQ: 31386232Stmm start = end = isa_route_intr_res(bus, start, end); 314117119Stmm if (start == PCI_INVALID_IRQ) 31586232Stmm return (NULL); 31686232Stmm break; 31786232Stmm } 31886232Stmm } 31986232Stmm 32086232Stmm rle->res = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, 32186232Stmm type, rid, start, end, count, flags); 32286232Stmm 32386232Stmm /* 32486232Stmm * Record the new range. 32586232Stmm */ 32686232Stmm if (rle->res != NULL) { 32786232Stmm rle->start = rman_get_start(rle->res) - base; 32886232Stmm rle->end = rman_get_end(rle->res) - base; 32986232Stmm rle->count = count; 33086232Stmm } 33186232Stmm 33286232Stmm return (rle->res); 33386232Stmm} 33486232Stmm 33586232Stmmint 33686232Stmmisa_release_resource(device_t bus, device_t child, int type, int rid, 33786232Stmm struct resource *res) 33886232Stmm{ 33986232Stmm struct isa_device* idev = DEVTOISA(child); 34086232Stmm struct resource_list *rl = &idev->id_resources; 34186232Stmm 34286232Stmm return (resource_list_release(rl, bus, child, type, rid, res)); 34386232Stmm} 34486232Stmm 34586232Stmmint 34686232Stmmisa_setup_intr(device_t dev, device_t child, 34786232Stmm struct resource *irq, int flags, 34886232Stmm driver_intr_t *intr, void *arg, void **cookiep) 34986232Stmm{ 35086232Stmm 35186232Stmm /* 35286232Stmm * Just pass through. This is going to be handled by either one of 35386232Stmm * the parent PCI buses or the nexus device. 35486232Stmm * The interrupt was routed at allocation time. 35586232Stmm */ 35686232Stmm return (BUS_SETUP_INTR(device_get_parent(dev), child, irq, flags, intr, 35786232Stmm arg, cookiep)); 35886232Stmm} 35986232Stmm 36086232Stmmint 36186232Stmmisa_teardown_intr(device_t dev, device_t child, 36286232Stmm struct resource *irq, void *cookie) 36386232Stmm{ 36486232Stmm 36586232Stmm return (BUS_TEARDOWN_INTR(device_get_parent(dev), child, irq, cookie)); 36686232Stmm} 367