isa.c revision 127146
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 127146 2004-03-17 21:45:55Z jmg $ 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 /* 205127146Sjmg * Consider adding a resource definition. 20686232Stmm */ 20786232Stmm int passthrough = (device_get_parent(child) != bus); 20886232Stmm int isdefault = (start == 0UL && end == ~0UL); 20986232Stmm struct isa_device* idev = DEVTOISA(child); 21086232Stmm struct resource_list *rl = &idev->id_resources; 21186232Stmm struct resource_list_entry *rle; 21286232Stmm u_long base, limit; 21386232Stmm 21486232Stmm if (!passthrough && !isdefault) { 21586232Stmm rle = resource_list_find(rl, type, *rid); 21686232Stmm if (!rle) { 21786232Stmm if (*rid < 0) 21886232Stmm return 0; 21986232Stmm switch (type) { 22086232Stmm case SYS_RES_IRQ: 22186232Stmm if (*rid >= ISA_NIRQ) 22286232Stmm return 0; 22386232Stmm break; 22486232Stmm case SYS_RES_DRQ: 22586232Stmm if (*rid >= ISA_NDRQ) 22686232Stmm return 0; 22786232Stmm break; 22886232Stmm case SYS_RES_MEMORY: 22986232Stmm if (*rid >= ISA_NMEM) 23086232Stmm return 0; 23186232Stmm break; 23286232Stmm case SYS_RES_IOPORT: 23386232Stmm if (*rid >= ISA_NPORT) 23486232Stmm return 0; 23586232Stmm break; 23686232Stmm default: 23786232Stmm return 0; 23886232Stmm } 23986232Stmm resource_list_add(rl, type, *rid, start, end, count); 24086232Stmm } 24186232Stmm } 24286232Stmm 24386232Stmm /* 24486232Stmm * Add the base, change default allocations to be between base and 24586232Stmm * limit, and reject allocations if a resource type is not enabled. 24686232Stmm */ 24786232Stmm base = limit = 0; 24886232Stmm switch(type) { 24986232Stmm case SYS_RES_MEMORY: 25086232Stmm if (isa_mem_bt == NULL) 25186232Stmm return (NULL); 25286232Stmm base = isa_mem_base; 25386232Stmm limit = base + isa_mem_limit; 25486232Stmm break; 25586232Stmm case SYS_RES_IOPORT: 25686232Stmm if (isa_io_bt == NULL) 25786232Stmm return (NULL); 25886232Stmm base = isa_io_base; 25986232Stmm limit = base + isa_io_limit; 26086232Stmm break; 26186232Stmm case SYS_RES_IRQ: 26286232Stmm if (isdefault && passthrough) 26386232Stmm panic("isa_alloc_resource: cannot pass through default " 26486232Stmm "irq allocation"); 26586232Stmm if (!isdefault) { 26686232Stmm start = end = isa_route_intr_res(bus, start, end); 267117119Stmm if (start == PCI_INVALID_IRQ) 26886232Stmm return (NULL); 26986232Stmm } 27086232Stmm break; 27186232Stmm default: 27286232Stmm panic("isa_alloc_resource: unsupported resource type %d", type); 27386232Stmm } 27486232Stmm if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { 27586232Stmm start = ulmin(start + base, limit); 27686232Stmm end = ulmin(end + base, limit); 27786232Stmm } 278117119Stmm 27986232Stmm /* 28086232Stmm * This inlines a modified resource_list_alloc(); this is needed 28190612Stmm * because the resources need to have offsets added to them, which 28290612Stmm * cannot be done beforehand without patching the resource list entries 28386232Stmm * (which is ugly). 28486232Stmm */ 28586232Stmm if (passthrough) { 28686232Stmm return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child, 28786232Stmm type, rid, start, end, count, flags)); 28886232Stmm } 28986232Stmm 29086232Stmm rle = resource_list_find(rl, type, *rid); 29186232Stmm if (rle == NULL) 29286232Stmm return (NULL); /* no resource of that type/rid */ 29386232Stmm 29486232Stmm if (rle->res != NULL) 29586232Stmm panic("isa_alloc_resource: resource entry is busy"); 29686232Stmm 29786232Stmm if (isdefault) { 29886232Stmm start = rle->start; 29986232Stmm count = ulmax(count, rle->count); 30086232Stmm end = ulmax(rle->end, start + count - 1); 30186232Stmm switch (type) { 30286232Stmm case SYS_RES_MEMORY: 30386232Stmm case SYS_RES_IOPORT: 30486232Stmm start += base; 30586232Stmm end += base; 30686232Stmm if (!INRANGE(start, base, limit) || 30793681Stmm !INRANGE(end, base, limit)) 30893681Stmm return (NULL); 30986232Stmm break; 31086232Stmm case SYS_RES_IRQ: 31186232Stmm start = end = isa_route_intr_res(bus, start, end); 312117119Stmm if (start == PCI_INVALID_IRQ) 31386232Stmm return (NULL); 31486232Stmm break; 31586232Stmm } 31686232Stmm } 31786232Stmm 31886232Stmm rle->res = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, 31986232Stmm type, rid, start, end, count, flags); 32086232Stmm 32186232Stmm /* 32286232Stmm * Record the new range. 32386232Stmm */ 32486232Stmm if (rle->res != NULL) { 32586232Stmm rle->start = rman_get_start(rle->res) - base; 32686232Stmm rle->end = rman_get_end(rle->res) - base; 32786232Stmm rle->count = count; 32886232Stmm } 32986232Stmm 33086232Stmm return (rle->res); 33186232Stmm} 33286232Stmm 33386232Stmmint 33486232Stmmisa_release_resource(device_t bus, device_t child, int type, int rid, 33586232Stmm struct resource *res) 33686232Stmm{ 33786232Stmm struct isa_device* idev = DEVTOISA(child); 33886232Stmm struct resource_list *rl = &idev->id_resources; 33986232Stmm 34086232Stmm return (resource_list_release(rl, bus, child, type, rid, res)); 34186232Stmm} 34286232Stmm 34386232Stmmint 34486232Stmmisa_setup_intr(device_t dev, device_t child, 34586232Stmm struct resource *irq, int flags, 34686232Stmm driver_intr_t *intr, void *arg, void **cookiep) 34786232Stmm{ 34886232Stmm 34986232Stmm /* 35086232Stmm * Just pass through. This is going to be handled by either one of 35186232Stmm * the parent PCI buses or the nexus device. 35286232Stmm * The interrupt was routed at allocation time. 35386232Stmm */ 35486232Stmm return (BUS_SETUP_INTR(device_get_parent(dev), child, irq, flags, intr, 35586232Stmm arg, cookiep)); 35686232Stmm} 35786232Stmm 35886232Stmmint 35986232Stmmisa_teardown_intr(device_t dev, device_t child, 36086232Stmm struct resource *irq, void *cookie) 36186232Stmm{ 36286232Stmm 36386232Stmm return (BUS_TEARDOWN_INTR(device_get_parent(dev), child, irq, cookie)); 36486232Stmm} 365