isa.c revision 86232
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 86232 2001-11-09 20:21:21Z 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> 5486232Stmm#include <machine/resource.h> 5586232Stmm 5686232Stmm#include <sparc64/pci/ofw_pci.h> 5786232Stmm#include <sparc64/isa/ofw_isa.h> 5886232Stmm 5986232Stmm#include "sparcbus_if.h" 6086232Stmm 6186232Stmm/* There can be only one ISA bus, so it is safe to use globals. */ 6286232Stmmbus_space_tag_t isa_io_bt = NULL; 6386232Stmmbus_space_handle_t isa_io_hdl; 6486232Stmmbus_space_tag_t isa_mem_bt = NULL; 6586232Stmmbus_space_handle_t isa_mem_hdl; 6686232Stmm 6786232Stmmu_int64_t isa_io_base; 6886232Stmmu_int64_t isa_io_limit; 6986232Stmmu_int64_t isa_mem_base; 7086232Stmmu_int64_t isa_mem_limit; 7186232Stmm 7286232Stmmdevice_t isa_bus_device; 7386232Stmm 7486232Stmmstatic struct ofw_pci_register isab_reg; 7586232Stmmstatic struct ofw_pci_imap *isab_imap; 7686232Stmmstatic int isab_nimap; 7786232Stmmstatic struct ofw_pci_imap_msk isab_imap_msk; 7886232Stmmstatic phandle_t isab_node; 7986232Stmm 8086232Stmm/* 8186232Stmm * XXX: This is really partly partly PCI-specific, but unfortunately is 8286232Stmm * differently enough to have to duplicate it here... 8386232Stmm */ 8486232Stmm#define ISAB_RANGE_PHYS(r) \ 8586232Stmm (((u_int64_t)(r)->phys_mid << 32) | (u_int64_t)(r)->phys_lo) 8686232Stmm#define ISAB_RANGE_SPACE(r) (((r)->phys_hi >> 24) & 0x03) 8786232Stmm 8886232Stmm#define ISAR_SPACE_IO 0x01 8986232Stmm#define ISAR_SPACE_MEM 0x02 9086232Stmm 9186232Stmm#define INRANGE(x, start, end) ((x) >= (start) && (x) <= (end)) 9286232Stmm 9386232Stmmstatic int isa_route_intr_res(device_t, u_long, u_long); 9486232Stmm 9586232Stmmstatic int isa_ino[8]; 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--) { 10586232Stmm pending <<= 1; 10686232Stmm if (isa_ino[i] != 255) { 10786232Stmm pending |= (SPARCBUS_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; 11986232Stmm struct isa_ranges *br; 12086232Stmm int nbr, i; 12186232Stmm 12286232Stmm /* The parent of the bus must be a PCI-ISA bridge. */ 12386232Stmm bridge = device_get_parent(dev); 12486232Stmm isab_node = ofw_pci_find_node(pci_get_bus(bridge), pci_get_slot(bridge), 12586232Stmm pci_get_function(bridge)); 12686232Stmm if (OF_getprop(isab_node, "reg", &isab_reg, sizeof(isab_reg)) < 0) 12786232Stmm panic("isa_init: cannot get bridge reg property"); 12886232Stmm nbr = OF_getprop_alloc(isab_node, "ranges", sizeof(*br), (void **)&br); 12986232Stmm if (nbr <= 0) 13086232Stmm panic("isa_init: cannot get bridge range property"); 13186232Stmm node = isab_node; 13286232Stmm isab_nimap = ofw_pci_find_imap(node, &isab_imap, &isab_imap_msk); 13386232Stmm if (isab_nimap == -1) 13486232Stmm panic("isa_init: could not find interrupt-map"); 13586232Stmm for (i = 0; i < 8; i++) { 13686232Stmm isa_ino[i] = ofw_pci_route_intr2(i, &isab_reg, isab_imap, 13786232Stmm isab_nimap, &isab_imap_msk); 13886232Stmm } 13986232Stmm 14086232Stmm 14186232Stmm for (nbr -= 1; nbr >= 0; nbr--) { 14286232Stmm switch(ISAB_RANGE_SPACE(br + nbr)) { 14386232Stmm case ISAR_SPACE_IO: 14486232Stmm /* This is probably always 0. */ 14586232Stmm isa_io_base = ISAB_RANGE_PHYS(&br[nbr]); 14686232Stmm isa_io_limit = br[nbr].size; 14786232Stmm isa_io_hdl = SPARCBUS_GET_BUS_HANDLE(bridge, SBBT_IO, 14886232Stmm isa_io_base, &isa_io_bt); 14986232Stmm break; 15086232Stmm case ISAR_SPACE_MEM: 15186232Stmm /* This is probably always 0. */ 15286232Stmm isa_mem_base = ISAB_RANGE_PHYS(&br[nbr]); 15386232Stmm isa_mem_limit = br[nbr].size; 15486232Stmm isa_mem_hdl = SPARCBUS_GET_BUS_HANDLE(bridge, SBBT_MEM, 15586232Stmm isa_mem_base, &isa_mem_bt); 15686232Stmm break; 15786232Stmm } 15886232Stmm } 15986232Stmm free(br, M_OFWPROP); 16086232Stmm} 16186232Stmm 16286232Stmmstatic int 16386232Stmmisa_route_intr_res(device_t bus, u_long start, u_long end) 16486232Stmm{ 16586232Stmm int res; 16686232Stmm 16786232Stmm if (start != end) { 16886232Stmm panic("isa_route_intr_res: allocation of interrupt range not " 16986232Stmm "supported (0x%lx - 0x%lx)", start, end); 17086232Stmm } 17186232Stmm res = ofw_pci_route_intr2(start, &isab_reg, isab_imap, isab_nimap, 17286232Stmm &isab_imap_msk); 17386232Stmm if (res == 255) 17486232Stmm device_printf(bus, "could not map interrupt %d\n", res); 17586232Stmm return (res); 17686232Stmm} 17786232Stmm 17886232Stmmstruct resource * 17986232Stmmisa_alloc_resource(device_t bus, device_t child, int type, int *rid, 18086232Stmm u_long start, u_long end, u_long count, u_int flags) 18186232Stmm{ 18286232Stmm /* 18386232Stmm * Consider adding a resource definition. We allow rid 0-1 for 18486232Stmm * irq and drq, 0-3 for memory and 0-7 for ports which is 18586232Stmm * sufficient for isapnp. 18686232Stmm */ 18786232Stmm int passthrough = (device_get_parent(child) != bus); 18886232Stmm int isdefault = (start == 0UL && end == ~0UL); 18986232Stmm struct isa_device* idev = DEVTOISA(child); 19086232Stmm struct resource_list *rl = &idev->id_resources; 19186232Stmm struct resource_list_entry *rle; 19286232Stmm u_long base, limit; 19386232Stmm 19486232Stmm if (!passthrough && !isdefault) { 19586232Stmm rle = resource_list_find(rl, type, *rid); 19686232Stmm if (!rle) { 19786232Stmm if (*rid < 0) 19886232Stmm return 0; 19986232Stmm switch (type) { 20086232Stmm case SYS_RES_IRQ: 20186232Stmm if (*rid >= ISA_NIRQ) 20286232Stmm return 0; 20386232Stmm break; 20486232Stmm case SYS_RES_DRQ: 20586232Stmm if (*rid >= ISA_NDRQ) 20686232Stmm return 0; 20786232Stmm break; 20886232Stmm case SYS_RES_MEMORY: 20986232Stmm if (*rid >= ISA_NMEM) 21086232Stmm return 0; 21186232Stmm break; 21286232Stmm case SYS_RES_IOPORT: 21386232Stmm if (*rid >= ISA_NPORT) 21486232Stmm return 0; 21586232Stmm break; 21686232Stmm default: 21786232Stmm return 0; 21886232Stmm } 21986232Stmm resource_list_add(rl, type, *rid, start, end, count); 22086232Stmm } 22186232Stmm } 22286232Stmm 22386232Stmm /* 22486232Stmm * Add the base, change default allocations to be between base and 22586232Stmm * limit, and reject allocations if a resource type is not enabled. 22686232Stmm */ 22786232Stmm base = limit = 0; 22886232Stmm switch(type) { 22986232Stmm case SYS_RES_MEMORY: 23086232Stmm if (isa_mem_bt == NULL) 23186232Stmm return (NULL); 23286232Stmm base = isa_mem_base; 23386232Stmm limit = base + isa_mem_limit; 23486232Stmm break; 23586232Stmm case SYS_RES_IOPORT: 23686232Stmm if (isa_io_bt == NULL) 23786232Stmm return (NULL); 23886232Stmm base = isa_io_base; 23986232Stmm limit = base + isa_io_limit; 24086232Stmm break; 24186232Stmm case SYS_RES_IRQ: 24286232Stmm if (isdefault && passthrough) 24386232Stmm panic("isa_alloc_resource: cannot pass through default " 24486232Stmm "irq allocation"); 24586232Stmm if (!isdefault) { 24686232Stmm start = end = isa_route_intr_res(bus, start, end); 24786232Stmm if (start == 255) 24886232Stmm return (NULL); 24986232Stmm } 25086232Stmm break; 25186232Stmm default: 25286232Stmm panic("isa_alloc_resource: unsupported resource type %d", type); 25386232Stmm } 25486232Stmm if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { 25586232Stmm start = ulmin(start + base, limit); 25686232Stmm end = ulmin(end + base, limit); 25786232Stmm } 25886232Stmm 25986232Stmm /* 26086232Stmm * This inlines a modified resource_list_alloc(); this is needed 26186232Stmm * because the resources need to have offsets added to them, whcih 26286232Stmm * cannot be done beforehand without patching the resource list entires 26386232Stmm * (which is ugly). 26486232Stmm */ 26586232Stmm if (passthrough) { 26686232Stmm return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child, 26786232Stmm type, rid, start, end, count, flags)); 26886232Stmm } 26986232Stmm 27086232Stmm rle = resource_list_find(rl, type, *rid); 27186232Stmm if (rle == NULL) 27286232Stmm return (NULL); /* no resource of that type/rid */ 27386232Stmm 27486232Stmm if (rle->res != NULL) 27586232Stmm panic("isa_alloc_resource: resource entry is busy"); 27686232Stmm 27786232Stmm if (isdefault) { 27886232Stmm start = rle->start; 27986232Stmm count = ulmax(count, rle->count); 28086232Stmm end = ulmax(rle->end, start + count - 1); 28186232Stmm switch (type) { 28286232Stmm case SYS_RES_MEMORY: 28386232Stmm case SYS_RES_IOPORT: 28486232Stmm start += base; 28586232Stmm end += base; 28686232Stmm if (!INRANGE(start, base, limit) || 28786232Stmm !INRANGE(end, base, limit)) { 28886232Stmm panic("isa_alloc_resource: resource list entry " 28986232Stmm "out of bus range (0x%lx - 0x%lx not in " 29086232Stmm "0x%lx - 0x%lx)", start, end, base, limit); 29186232Stmm } 29286232Stmm break; 29386232Stmm case SYS_RES_IRQ: 29486232Stmm start = end = isa_route_intr_res(bus, start, end); 29586232Stmm if (start == 255) 29686232Stmm return (NULL); 29786232Stmm break; 29886232Stmm } 29986232Stmm } 30086232Stmm 30186232Stmm rle->res = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, 30286232Stmm type, rid, start, end, count, flags); 30386232Stmm 30486232Stmm /* 30586232Stmm * Record the new range. 30686232Stmm */ 30786232Stmm if (rle->res != NULL) { 30886232Stmm rle->start = rman_get_start(rle->res) - base; 30986232Stmm rle->end = rman_get_end(rle->res) - base; 31086232Stmm rle->count = count; 31186232Stmm } 31286232Stmm 31386232Stmm return (rle->res); 31486232Stmm} 31586232Stmm 31686232Stmmint 31786232Stmmisa_release_resource(device_t bus, device_t child, int type, int rid, 31886232Stmm struct resource *res) 31986232Stmm{ 32086232Stmm struct isa_device* idev = DEVTOISA(child); 32186232Stmm struct resource_list *rl = &idev->id_resources; 32286232Stmm 32386232Stmm return (resource_list_release(rl, bus, child, type, rid, res)); 32486232Stmm} 32586232Stmm 32686232Stmmint 32786232Stmmisa_setup_intr(device_t dev, device_t child, 32886232Stmm struct resource *irq, int flags, 32986232Stmm driver_intr_t *intr, void *arg, void **cookiep) 33086232Stmm{ 33186232Stmm 33286232Stmm /* 33386232Stmm * Just pass through. This is going to be handled by either one of 33486232Stmm * the parent PCI buses or the nexus device. 33586232Stmm * The interrupt was routed at allocation time. 33686232Stmm */ 33786232Stmm return (BUS_SETUP_INTR(device_get_parent(dev), child, irq, flags, intr, 33886232Stmm arg, cookiep)); 33986232Stmm} 34086232Stmm 34186232Stmmint 34286232Stmmisa_teardown_intr(device_t dev, device_t child, 34386232Stmm struct resource *irq, void *cookie) 34486232Stmm{ 34586232Stmm 34686232Stmm return (BUS_TEARDOWN_INTR(device_get_parent(dev), child, irq, cookie)); 34786232Stmm} 348