isa_common.c revision 50910
147398Sdfr/*- 247398Sdfr * Copyright (c) 1999 Doug Rabson 347398Sdfr * All rights reserved. 447398Sdfr * 547398Sdfr * Redistribution and use in source and binary forms, with or without 647398Sdfr * modification, are permitted provided that the following conditions 747398Sdfr * are met: 847398Sdfr * 1. Redistributions of source code must retain the above copyright 947398Sdfr * notice, this list of conditions and the following disclaimer. 1047398Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1147398Sdfr * notice, this list of conditions and the following disclaimer in the 1247398Sdfr * documentation and/or other materials provided with the distribution. 1347398Sdfr * 1447398Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1547398Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1647398Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1747398Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1847398Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1947398Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2047398Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2147398Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2247398Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2347398Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2447398Sdfr * SUCH DAMAGE. 2547398Sdfr * 2650477Speter * $FreeBSD: head/sys/isa/isa_common.c 50910 1999-09-04 14:43:35Z dfr $ 2747398Sdfr */ 2847398Sdfr/* 2947398Sdfr * Modifications for Intel architecture by Garrett A. Wollman. 3047398Sdfr * Copyright 1998 Massachusetts Institute of Technology 3147398Sdfr * 3247398Sdfr * Permission to use, copy, modify, and distribute this software and 3347398Sdfr * its documentation for any purpose and without fee is hereby 3447398Sdfr * granted, provided that both the above copyright notice and this 3547398Sdfr * permission notice appear in all copies, that both the above 3647398Sdfr * copyright notice and this permission notice appear in all 3747398Sdfr * supporting documentation, and that the name of M.I.T. not be used 3847398Sdfr * in advertising or publicity pertaining to distribution of the 3947398Sdfr * software without specific, written prior permission. M.I.T. makes 4047398Sdfr * no representations about the suitability of this software for any 4147398Sdfr * purpose. It is provided "as is" without express or implied 4247398Sdfr * warranty. 4347398Sdfr * 4447398Sdfr * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 4547398Sdfr * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 4647398Sdfr * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 4747398Sdfr * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 4847398Sdfr * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 4947398Sdfr * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 5047398Sdfr * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 5147398Sdfr * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 5247398Sdfr * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 5347398Sdfr * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 5447398Sdfr * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 5547398Sdfr * SUCH DAMAGE. 5647398Sdfr */ 5747398Sdfr 5847398Sdfr/* 5947398Sdfr * Parts of the ISA bus implementation common to all architectures. 6047398Sdfr */ 6147398Sdfr 6247398Sdfr#include <sys/param.h> 6347398Sdfr#include <sys/systm.h> 6447398Sdfr#include <sys/kernel.h> 6547398Sdfr#include <sys/bus.h> 6647398Sdfr#include <sys/malloc.h> 6747398Sdfr#include <sys/module.h> 6847398Sdfr#include <machine/bus.h> 6947398Sdfr#include <sys/rman.h> 7047398Sdfr 7147398Sdfr#include <machine/resource.h> 7247398Sdfr 7347398Sdfr#include <isa/isavar.h> 7447398Sdfr#include <isa/isa_common.h> 7547398Sdfr#ifdef __alpha__ /* XXX workaround a stupid warning */ 7647398Sdfr#include <alpha/isa/isavar.h> 7747398Sdfr#endif 7847398Sdfr 7947398SdfrMALLOC_DEFINE(M_ISADEV, "isadev", "ISA device"); 8047398Sdfr 8147398Sdfrstatic devclass_t isa_devclass; 8247398Sdfr 8347398Sdfr/* 8447398Sdfr * At 'probe' time, we add all the devices which we know about to the 8547398Sdfr * bus. The generic attach routine will probe and attach them if they 8647398Sdfr * are alive. 8747398Sdfr */ 8847398Sdfrstatic int 8947398Sdfrisa_probe(device_t dev) 9047398Sdfr{ 9147398Sdfr device_set_desc(dev, "ISA bus"); 9247398Sdfr isa_init(); /* Allow machdep code to initialise */ 9347398Sdfr return bus_generic_probe(dev); 9447398Sdfr} 9547398Sdfr 9647398Sdfrextern device_t isa_bus_device; 9747398Sdfr 9847398Sdfrstatic int 9947398Sdfrisa_attach(device_t dev) 10047398Sdfr{ 10147398Sdfr /* 10250769Sdfr * Arrange for isa_probe_children(dev) to be called later. XXX 10347398Sdfr */ 10447398Sdfr isa_bus_device = dev; 10547398Sdfr return 0; 10647398Sdfr} 10747398Sdfr 10847398Sdfr/* 10950769Sdfr * Find a working set of memory regions for a child using the ranges 11050769Sdfr * in *config and return the regions in *result. Returns non-zero if 11150769Sdfr * a set of ranges was found. 11250769Sdfr */ 11350769Sdfrstatic int 11450769Sdfrisa_find_memory(device_t child, 11550769Sdfr struct isa_config *config, 11650769Sdfr struct isa_config *result) 11750769Sdfr{ 11850769Sdfr device_t dev = device_get_parent(child); 11950769Sdfr int success, i; 12050769Sdfr struct resource *res[ISA_NMEM]; 12150769Sdfr 12250769Sdfr /* 12350769Sdfr * First clear out any existing resource definitions. 12450769Sdfr */ 12550769Sdfr for (i = 0; i < ISA_NMEM; i++) { 12650769Sdfr ISA_DELETE_RESOURCE(dev, child, SYS_RES_MEMORY, i); 12750769Sdfr res[i] = NULL; 12850769Sdfr } 12950769Sdfr 13050769Sdfr success = 1; 13150769Sdfr result->ic_nmem = config->ic_nmem; 13250769Sdfr for (i = 0; i < config->ic_nmem; i++) { 13350769Sdfr u_int32_t start, end, size, align; 13450769Sdfr for (start = config->ic_mem[i].ir_start, 13550769Sdfr end = config->ic_mem[i].ir_end, 13650769Sdfr size = config->ic_mem[i].ir_size, 13750769Sdfr align = config->ic_mem[i].ir_align; 13850769Sdfr start + size - 1 <= end; 13950769Sdfr start += align) { 14050769Sdfr ISA_SET_RESOURCE(dev, child, SYS_RES_MEMORY, i, 14150769Sdfr start, size); 14250769Sdfr res[i] = bus_alloc_resource(child, 14350769Sdfr SYS_RES_MEMORY, &i, 14450769Sdfr 0, ~0, 1, RF_ACTIVE); 14550769Sdfr if (res[i]) { 14650769Sdfr result->ic_mem[i].ir_start = start; 14750769Sdfr result->ic_mem[i].ir_end = start + size - 1; 14850769Sdfr result->ic_mem[i].ir_size = size; 14950769Sdfr result->ic_mem[i].ir_align = align; 15050769Sdfr break; 15150769Sdfr } 15250769Sdfr } 15350769Sdfr 15450769Sdfr /* 15550769Sdfr * If we didn't find a place for memory range i, then 15650769Sdfr * give up now. 15750769Sdfr */ 15850769Sdfr if (!res[i]) { 15950769Sdfr success = 0; 16050769Sdfr break; 16150769Sdfr } 16250769Sdfr } 16350769Sdfr 16450769Sdfr for (i = 0; i < ISA_NMEM; i++) { 16550769Sdfr if (res[i]) 16650769Sdfr bus_release_resource(child, SYS_RES_MEMORY, 16750769Sdfr i, res[i]); 16850769Sdfr } 16950769Sdfr 17050769Sdfr return success; 17150769Sdfr} 17250769Sdfr 17350769Sdfr/* 17450769Sdfr * Find a working set of port regions for a child using the ranges 17550769Sdfr * in *config and return the regions in *result. Returns non-zero if 17650769Sdfr * a set of ranges was found. 17750769Sdfr */ 17850769Sdfrstatic int 17950769Sdfrisa_find_port(device_t child, 18050769Sdfr struct isa_config *config, 18150769Sdfr struct isa_config *result) 18250769Sdfr{ 18350769Sdfr device_t dev = device_get_parent(child); 18450769Sdfr int success, i; 18550769Sdfr struct resource *res[ISA_NPORT]; 18650769Sdfr 18750769Sdfr /* 18850769Sdfr * First clear out any existing resource definitions. 18950769Sdfr */ 19050769Sdfr for (i = 0; i < ISA_NPORT; i++) { 19150769Sdfr ISA_DELETE_RESOURCE(dev, child, SYS_RES_IOPORT, i); 19250769Sdfr res[i] = NULL; 19350769Sdfr } 19450769Sdfr 19550769Sdfr success = 1; 19650769Sdfr result->ic_nport = config->ic_nport; 19750769Sdfr for (i = 0; i < config->ic_nport; i++) { 19850769Sdfr u_int32_t start, end, size, align; 19950769Sdfr for (start = config->ic_port[i].ir_start, 20050769Sdfr end = config->ic_port[i].ir_end, 20150769Sdfr size = config->ic_port[i].ir_size, 20250769Sdfr align = config->ic_port[i].ir_align; 20350769Sdfr start + size - 1 <= end; 20450769Sdfr start += align) { 20550769Sdfr ISA_SET_RESOURCE(dev, child, SYS_RES_IOPORT, i, 20650769Sdfr start, size); 20750769Sdfr res[i] = bus_alloc_resource(child, 20850769Sdfr SYS_RES_IOPORT, &i, 20950769Sdfr 0, ~0, 1, RF_ACTIVE); 21050769Sdfr if (res[i]) { 21150769Sdfr result->ic_port[i].ir_start = start; 21250769Sdfr result->ic_port[i].ir_end = start + size - 1; 21350769Sdfr result->ic_port[i].ir_size = size; 21450769Sdfr result->ic_port[i].ir_align = align; 21550769Sdfr break; 21650769Sdfr } 21750769Sdfr } 21850769Sdfr 21950769Sdfr /* 22050769Sdfr * If we didn't find a place for port range i, then 22150769Sdfr * give up now. 22250769Sdfr */ 22350769Sdfr if (!res[i]) { 22450769Sdfr success = 0; 22550769Sdfr break; 22650769Sdfr } 22750769Sdfr } 22850769Sdfr 22950769Sdfr for (i = 0; i < ISA_NPORT; i++) { 23050769Sdfr if (res[i]) 23150769Sdfr bus_release_resource(child, SYS_RES_IOPORT, 23250769Sdfr i, res[i]); 23350769Sdfr } 23450769Sdfr 23550769Sdfr return success; 23650769Sdfr} 23750769Sdfr 23850769Sdfr/* 23950769Sdfr * Return the index of the first bit in the mask (or -1 if mask is empty. 24050769Sdfr */ 24150769Sdfrstatic int 24250769Sdfrfind_first_bit(u_int32_t mask) 24350769Sdfr{ 24450769Sdfr return ffs(mask) - 1; 24550769Sdfr} 24650769Sdfr 24750769Sdfr/* 24850769Sdfr * Return the index of the next bit in the mask, or -1 if there are no more. 24950769Sdfr */ 25050769Sdfrstatic int 25150769Sdfrfind_next_bit(u_int32_t mask, int bit) 25250769Sdfr{ 25350769Sdfr bit++; 25450769Sdfr while (bit < 32 && !(mask & (1 << bit))) 25550769Sdfr bit++; 25650769Sdfr if (bit != 32) 25750769Sdfr return bit; 25850769Sdfr return -1; 25950769Sdfr} 26050769Sdfr 26150769Sdfr/* 26250769Sdfr * Find a working set of irqs for a child using the masks in *config 26350769Sdfr * and return the regions in *result. Returns non-zero if a set of 26450769Sdfr * irqs was found. 26550769Sdfr */ 26650769Sdfrstatic int 26750769Sdfrisa_find_irq(device_t child, 26850769Sdfr struct isa_config *config, 26950769Sdfr struct isa_config *result) 27050769Sdfr{ 27150769Sdfr device_t dev = device_get_parent(child); 27250769Sdfr int success, i; 27350769Sdfr struct resource *res[ISA_NIRQ]; 27450769Sdfr 27550769Sdfr /* 27650769Sdfr * First clear out any existing resource definitions. 27750769Sdfr */ 27850769Sdfr for (i = 0; i < ISA_NIRQ; i++) { 27950769Sdfr ISA_DELETE_RESOURCE(dev, child, SYS_RES_IRQ, i); 28050769Sdfr res[i] = NULL; 28150769Sdfr } 28250769Sdfr 28350769Sdfr success = 1; 28450769Sdfr result->ic_nirq = config->ic_nirq; 28550769Sdfr for (i = 0; i < config->ic_nirq; i++) { 28650769Sdfr u_int32_t mask = config->ic_irqmask[i]; 28750769Sdfr int irq; 28850769Sdfr for (irq = find_first_bit(mask); 28950769Sdfr irq != -1; 29050769Sdfr irq = find_next_bit(mask, irq)) { 29150769Sdfr ISA_SET_RESOURCE(dev, child, SYS_RES_IRQ, i, 29250769Sdfr irq, 1); 29350769Sdfr res[i] = bus_alloc_resource(child, 29450769Sdfr SYS_RES_IRQ, &i, 29550769Sdfr 0, ~0, 1, RF_ACTIVE); 29650769Sdfr if (res[i]) { 29750769Sdfr result->ic_irqmask[i] = (1 << irq); 29850769Sdfr break; 29950769Sdfr } 30050769Sdfr } 30150769Sdfr 30250769Sdfr /* 30350769Sdfr * If we didn't find a place for irq range i, then 30450769Sdfr * give up now. 30550769Sdfr */ 30650769Sdfr if (!res[i]) { 30750769Sdfr success = 0; 30850769Sdfr break; 30950769Sdfr } 31050769Sdfr } 31150769Sdfr 31250769Sdfr for (i = 0; i < ISA_NIRQ; i++) { 31350769Sdfr if (res[i]) 31450769Sdfr bus_release_resource(child, SYS_RES_IRQ, 31550769Sdfr i, res[i]); 31650769Sdfr } 31750769Sdfr 31850769Sdfr return success; 31950769Sdfr} 32050769Sdfr 32150769Sdfr/* 32250769Sdfr * Find a working set of drqs for a child using the masks in *config 32350769Sdfr * and return the regions in *result. Returns non-zero if a set of 32450769Sdfr * drqs was found. 32550769Sdfr */ 32650769Sdfrstatic int 32750769Sdfrisa_find_drq(device_t child, 32850769Sdfr struct isa_config *config, 32950769Sdfr struct isa_config *result) 33050769Sdfr{ 33150769Sdfr device_t dev = device_get_parent(child); 33250769Sdfr int success, i; 33350769Sdfr struct resource *res[ISA_NDRQ]; 33450769Sdfr 33550769Sdfr /* 33650769Sdfr * First clear out any existing resource definitions. 33750769Sdfr */ 33850769Sdfr for (i = 0; i < ISA_NDRQ; i++) { 33950769Sdfr ISA_DELETE_RESOURCE(dev, child, SYS_RES_DRQ, i); 34050769Sdfr res[i] = NULL; 34150769Sdfr } 34250769Sdfr 34350769Sdfr success = 1; 34450769Sdfr result->ic_ndrq = config->ic_ndrq; 34550769Sdfr for (i = 0; i < config->ic_ndrq; i++) { 34650769Sdfr u_int32_t mask = config->ic_drqmask[i]; 34750769Sdfr int drq; 34850769Sdfr for (drq = find_first_bit(mask); 34950769Sdfr drq != -1; 35050769Sdfr drq = find_next_bit(mask, drq)) { 35150769Sdfr ISA_SET_RESOURCE(dev, child, SYS_RES_DRQ, i, 35250769Sdfr drq, 1); 35350769Sdfr res[i] = bus_alloc_resource(child, 35450769Sdfr SYS_RES_DRQ, &i, 35550769Sdfr 0, ~0, 1, RF_ACTIVE); 35650769Sdfr if (res[i]) { 35750769Sdfr result->ic_drqmask[i] = (1 << drq); 35850769Sdfr break; 35950769Sdfr } 36050769Sdfr } 36150769Sdfr 36250769Sdfr /* 36350769Sdfr * If we didn't find a place for drq range i, then 36450769Sdfr * give up now. 36550769Sdfr */ 36650769Sdfr if (!res[i]) { 36750769Sdfr success = 0; 36850769Sdfr break; 36950769Sdfr } 37050769Sdfr } 37150769Sdfr 37250769Sdfr for (i = 0; i < ISA_NDRQ; i++) { 37350769Sdfr if (res[i]) 37450769Sdfr bus_release_resource(child, SYS_RES_DRQ, 37550769Sdfr i, res[i]); 37650769Sdfr } 37750769Sdfr 37850769Sdfr return success; 37950769Sdfr} 38050769Sdfr 38150769Sdfr/* 38250769Sdfr * Attempt to find a working set of resources for a device. Return 38350769Sdfr * non-zero if a working configuration is found. 38450769Sdfr */ 38550769Sdfrstatic int 38650769Sdfrisa_assign_resources(device_t child) 38750769Sdfr{ 38850769Sdfr struct isa_device *idev = DEVTOISA(child); 38950769Sdfr struct isa_config_entry *ice; 39050769Sdfr struct isa_config config; 39150769Sdfr 39250769Sdfr bzero(&config, sizeof config); 39350769Sdfr TAILQ_FOREACH(ice, &idev->id_configs, ice_link) { 39450769Sdfr if (!isa_find_memory(child, &ice->ice_config, &config)) 39550769Sdfr continue; 39650769Sdfr if (!isa_find_port(child, &ice->ice_config, &config)) 39750769Sdfr continue; 39850769Sdfr if (!isa_find_irq(child, &ice->ice_config, &config)) 39950769Sdfr continue; 40050769Sdfr if (!isa_find_drq(child, &ice->ice_config, &config)) 40150769Sdfr continue; 40250769Sdfr 40350769Sdfr /* 40450769Sdfr * A working configuration was found enable the device 40550769Sdfr * with this configuration. 40650769Sdfr */ 40750769Sdfr if (idev->id_config_cb) { 40850769Sdfr idev->id_config_cb(idev->id_config_arg, 40950769Sdfr &config, 1); 41050769Sdfr return 1; 41150769Sdfr } 41250769Sdfr } 41350769Sdfr 41450769Sdfr /* 41550769Sdfr * Disable the device. 41650769Sdfr */ 41750769Sdfr bzero(&config, sizeof config); 41850769Sdfr if (idev->id_config_cb) 41950769Sdfr idev->id_config_cb(idev->id_config_arg, &config, 0); 42050769Sdfr device_disable(child); 42150769Sdfr 42250769Sdfr return 0; 42350769Sdfr} 42450769Sdfr 42550769Sdfr/* 42650769Sdfr * Called after other devices have initialised to probe for isa devices. 42750769Sdfr */ 42850769Sdfrvoid 42950769Sdfrisa_probe_children(device_t dev) 43050769Sdfr{ 43150769Sdfr device_t *children; 43250769Sdfr int nchildren, i; 43350769Sdfr 43450769Sdfr if (device_get_children(dev, &children, &nchildren)) 43550769Sdfr return; 43650769Sdfr 43750769Sdfr /* 43850769Sdfr * First probe all non-pnp devices so that they claim their 43950769Sdfr * resources first. 44050769Sdfr */ 44150769Sdfr for (i = 0; i < nchildren; i++) { 44250769Sdfr device_t child = children[i]; 44350769Sdfr struct isa_device *idev = DEVTOISA(child); 44450769Sdfr 44550769Sdfr if (TAILQ_FIRST(&idev->id_configs)) 44650769Sdfr continue; 44750769Sdfr 44850769Sdfr device_probe_and_attach(child); 44950769Sdfr } 45050769Sdfr 45150769Sdfr /* 45250769Sdfr * Next assign resource to pnp devices and probe them. 45350769Sdfr */ 45450769Sdfr for (i = 0; i < nchildren; i++) { 45550769Sdfr device_t child = children[i]; 45650769Sdfr struct isa_device* idev = DEVTOISA(child); 45750769Sdfr 45850769Sdfr if (!TAILQ_FIRST(&idev->id_configs)) 45950769Sdfr continue; 46050769Sdfr 46150769Sdfr if (isa_assign_resources(child)) { 46250769Sdfr struct resource_list_entry *rle; 46350769Sdfr 46450769Sdfr device_probe_and_attach(child); 46550769Sdfr 46650769Sdfr /* 46750769Sdfr * Claim any unallocated resources to keep other 46850769Sdfr * devices from using them. 46950769Sdfr */ 47050769Sdfr SLIST_FOREACH(rle, &idev->id_resources, link) { 47150769Sdfr if (!rle->res) { 47250769Sdfr int rid = rle->rid; 47350769Sdfr resource_list_alloc(dev, child, 47450769Sdfr rle->type, 47550769Sdfr &rid, 47650769Sdfr 0, ~0, 1, 47750769Sdfr RF_ACTIVE); 47850769Sdfr } 47950769Sdfr } 48050769Sdfr } 48150769Sdfr } 48250769Sdfr 48350769Sdfr free(children, M_TEMP); 48450769Sdfr} 48550769Sdfr 48650769Sdfr/* 48747398Sdfr * Add a new child with default ivars. 48847398Sdfr */ 48947398Sdfrstatic device_t 49047578Sdfrisa_add_child(device_t dev, int order, const char *name, int unit) 49147398Sdfr{ 49247398Sdfr struct isa_device *idev; 49347398Sdfr 49447398Sdfr idev = malloc(sizeof(struct isa_device), M_ISADEV, M_NOWAIT); 49547398Sdfr if (!idev) 49647398Sdfr return 0; 49747398Sdfr bzero(idev, sizeof *idev); 49847398Sdfr 49947398Sdfr resource_list_init(&idev->id_resources); 50047398Sdfr idev->id_flags = 0; 50150769Sdfr TAILQ_INIT(&idev->id_configs); 50247398Sdfr 50347578Sdfr return device_add_child_ordered(dev, order, name, unit, idev); 50447398Sdfr} 50547398Sdfr 50647398Sdfrstatic void 50747398Sdfrisa_print_resources(struct resource_list *rl, const char *name, int type, 50847398Sdfr const char *format) 50947398Sdfr{ 51050769Sdfr struct resource_list_entry *rle; 51150769Sdfr int printed; 51250769Sdfr int i; 51347398Sdfr 51450769Sdfr printed = 0; 51550769Sdfr for (i = 0; i < 16; i++) { 51650769Sdfr rle = resource_list_find(rl, type, i); 51750769Sdfr if (rle) { 51850769Sdfr if (printed == 0) 51950769Sdfr printf(" %s ", name); 52050769Sdfr else if (printed > 0) 52147398Sdfr printf(","); 52250769Sdfr printed++; 52350769Sdfr printf(format, rle->start); 52450769Sdfr if (rle->count > 1) { 52547398Sdfr printf("-"); 52650769Sdfr printf(format, rle->start + rle->count - 1); 52747398Sdfr } 52850769Sdfr } else if (i > 3) { 52950769Sdfr /* check the first few regardless */ 53050769Sdfr break; 53147398Sdfr } 53247398Sdfr } 53347398Sdfr} 53447398Sdfr 53549195Smdoddstatic int 53647398Sdfrisa_print_child(device_t bus, device_t dev) 53747398Sdfr{ 53847398Sdfr struct isa_device *idev = DEVTOISA(dev); 53947398Sdfr struct resource_list *rl = &idev->id_resources; 54049195Smdodd int retval = 0; 54147398Sdfr 54249195Smdodd retval += bus_print_child_header(bus, dev); 54349195Smdodd 54447398Sdfr if (SLIST_FIRST(rl) || idev->id_flags) 54549195Smdodd retval += printf(" at"); 54647398Sdfr 54749198Smdodd isa_print_resources(rl, "port", SYS_RES_IOPORT, "%#lx"); 54849198Smdodd isa_print_resources(rl, "iomem", SYS_RES_MEMORY, "%#lx"); 54949198Smdodd isa_print_resources(rl, "irq", SYS_RES_IRQ, "%ld"); 55049198Smdodd isa_print_resources(rl, "drq", SYS_RES_DRQ, "%ld"); 55147398Sdfr if (idev->id_flags) 55249195Smdodd retval += printf(" flags %#x", idev->id_flags); 55347398Sdfr 55449195Smdodd retval += bus_print_child_footer(bus, dev); 55549195Smdodd 55649195Smdodd return (retval); 55747398Sdfr} 55847398Sdfr 55947398Sdfrstatic int 56047398Sdfrisa_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result) 56147398Sdfr{ 56247398Sdfr struct isa_device* idev = DEVTOISA(dev); 56347398Sdfr struct resource_list *rl = &idev->id_resources; 56447398Sdfr struct resource_list_entry *rle; 56547398Sdfr 56647398Sdfr switch (index) { 56747398Sdfr case ISA_IVAR_PORT_0: 56847398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 0); 56947398Sdfr if (rle) 57047398Sdfr *result = rle->start; 57147398Sdfr else 57247398Sdfr *result = -1; 57347398Sdfr break; 57447398Sdfr 57547398Sdfr case ISA_IVAR_PORT_1: 57647398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 1); 57747398Sdfr if (rle) 57847398Sdfr *result = rle->start; 57947398Sdfr else 58047398Sdfr *result = -1; 58147398Sdfr break; 58247398Sdfr 58347398Sdfr case ISA_IVAR_PORTSIZE_0: 58447398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 0); 58547398Sdfr if (rle) 58647398Sdfr *result = rle->count; 58747398Sdfr else 58847398Sdfr *result = 0; 58947398Sdfr break; 59047398Sdfr 59147398Sdfr case ISA_IVAR_PORTSIZE_1: 59247398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 1); 59347398Sdfr if (rle) 59447398Sdfr *result = rle->count; 59547398Sdfr else 59647398Sdfr *result = 0; 59747398Sdfr break; 59847398Sdfr 59947398Sdfr case ISA_IVAR_MADDR_0: 60047398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 0); 60147398Sdfr if (rle) 60247398Sdfr *result = rle->start; 60347398Sdfr else 60447398Sdfr *result = -1; 60547398Sdfr break; 60647398Sdfr 60747398Sdfr case ISA_IVAR_MADDR_1: 60847398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 1); 60947398Sdfr if (rle) 61047398Sdfr *result = rle->start; 61147398Sdfr else 61247398Sdfr *result = -1; 61347398Sdfr break; 61447398Sdfr 61547398Sdfr case ISA_IVAR_MSIZE_0: 61647398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 0); 61747398Sdfr if (rle) 61847398Sdfr *result = rle->count; 61947398Sdfr else 62047398Sdfr *result = 0; 62147398Sdfr break; 62247398Sdfr 62347398Sdfr case ISA_IVAR_MSIZE_1: 62447398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 1); 62547398Sdfr if (rle) 62647398Sdfr *result = rle->count; 62747398Sdfr else 62847398Sdfr *result = 0; 62947398Sdfr break; 63047398Sdfr 63147398Sdfr case ISA_IVAR_IRQ_0: 63247398Sdfr rle = resource_list_find(rl, SYS_RES_IRQ, 0); 63347398Sdfr if (rle) 63447398Sdfr *result = rle->start; 63547398Sdfr else 63647398Sdfr *result = -1; 63747398Sdfr break; 63847398Sdfr 63947398Sdfr case ISA_IVAR_IRQ_1: 64047398Sdfr rle = resource_list_find(rl, SYS_RES_IRQ, 1); 64147398Sdfr if (rle) 64247398Sdfr *result = rle->start; 64347398Sdfr else 64447398Sdfr *result = -1; 64547398Sdfr break; 64647398Sdfr 64747398Sdfr case ISA_IVAR_DRQ_0: 64847398Sdfr rle = resource_list_find(rl, SYS_RES_DRQ, 0); 64947398Sdfr if (rle) 65047398Sdfr *result = rle->start; 65147398Sdfr else 65247398Sdfr *result = -1; 65347398Sdfr break; 65447398Sdfr 65547398Sdfr case ISA_IVAR_DRQ_1: 65647398Sdfr rle = resource_list_find(rl, SYS_RES_DRQ, 1); 65747398Sdfr if (rle) 65847398Sdfr *result = rle->start; 65947398Sdfr else 66047398Sdfr *result = -1; 66147398Sdfr break; 66247398Sdfr 66347398Sdfr case ISA_IVAR_FLAGS: 66447398Sdfr *result = idev->id_flags; 66547398Sdfr break; 66647613Sdfr 66747613Sdfr case ISA_IVAR_VENDORID: 66847613Sdfr *result = idev->id_vendorid; 66947613Sdfr break; 67047613Sdfr 67147613Sdfr case ISA_IVAR_SERIAL: 67247613Sdfr *result = idev->id_serial; 67347613Sdfr break; 67447613Sdfr 67547613Sdfr case ISA_IVAR_LOGICALID: 67647613Sdfr *result = idev->id_logicalid; 67747613Sdfr break; 67847613Sdfr 67947613Sdfr case ISA_IVAR_COMPATID: 68047613Sdfr *result = idev->id_compatid; 68147613Sdfr break; 68247613Sdfr 68347613Sdfr default: 68447613Sdfr return ENOENT; 68547398Sdfr } 68647613Sdfr 68747613Sdfr return 0; 68847398Sdfr} 68947398Sdfr 69047398Sdfrstatic int 69147398Sdfrisa_write_ivar(device_t bus, device_t dev, 69247398Sdfr int index, uintptr_t value) 69347398Sdfr{ 69447398Sdfr struct isa_device* idev = DEVTOISA(dev); 69547398Sdfr 69647398Sdfr switch (index) { 69747398Sdfr case ISA_IVAR_PORT_0: 69847398Sdfr case ISA_IVAR_PORT_1: 69947398Sdfr case ISA_IVAR_PORTSIZE_0: 70047398Sdfr case ISA_IVAR_PORTSIZE_1: 70147398Sdfr case ISA_IVAR_MADDR_0: 70247398Sdfr case ISA_IVAR_MADDR_1: 70347398Sdfr case ISA_IVAR_MSIZE_0: 70447398Sdfr case ISA_IVAR_MSIZE_1: 70547398Sdfr case ISA_IVAR_IRQ_0: 70647398Sdfr case ISA_IVAR_IRQ_1: 70747398Sdfr case ISA_IVAR_DRQ_0: 70847398Sdfr case ISA_IVAR_DRQ_1: 70947398Sdfr return EINVAL; 71047398Sdfr 71147398Sdfr case ISA_IVAR_FLAGS: 71247398Sdfr idev->id_flags = value; 71347398Sdfr break; 71447613Sdfr 71547613Sdfr case ISA_IVAR_VENDORID: 71647613Sdfr idev->id_vendorid = value; 71747613Sdfr break; 71847613Sdfr 71947613Sdfr case ISA_IVAR_SERIAL: 72047613Sdfr idev->id_serial = value; 72147613Sdfr break; 72247613Sdfr 72347613Sdfr case ISA_IVAR_LOGICALID: 72447613Sdfr idev->id_logicalid = value; 72547613Sdfr break; 72647613Sdfr 72747613Sdfr case ISA_IVAR_COMPATID: 72847613Sdfr idev->id_compatid = value; 72947613Sdfr break; 73047613Sdfr 73147398Sdfr default: 73247398Sdfr return (ENOENT); 73347398Sdfr } 73447613Sdfr 73547398Sdfr return (0); 73647398Sdfr} 73747398Sdfr 73850769Sdfr/* 73950769Sdfr * Free any resources which the driver missed or which we were holding for 74050769Sdfr * it (see isa_probe_children). 74150769Sdfr */ 74250769Sdfrstatic void 74350769Sdfrisa_child_detached(device_t dev, device_t child) 74450769Sdfr{ 74550769Sdfr struct isa_device* idev = DEVTOISA(child); 74650769Sdfr struct resource_list_entry *rle; 74750769Sdfr 74850769Sdfr SLIST_FOREACH(rle, &idev->id_resources, link) { 74950769Sdfr if (rle->res) 75050769Sdfr resource_list_release(dev, child, 75150769Sdfr rle->type, 75250769Sdfr rle->rid, 75350769Sdfr rle->res); 75450769Sdfr } 75550769Sdfr} 75650769Sdfr 75747398Sdfrstatic int 75847398Sdfrisa_set_resource(device_t dev, device_t child, int type, int rid, 75947398Sdfr u_long start, u_long count) 76047398Sdfr{ 76147398Sdfr struct isa_device* idev = DEVTOISA(child); 76247398Sdfr struct resource_list *rl = &idev->id_resources; 76347398Sdfr 76447398Sdfr if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY 76547398Sdfr && type != SYS_RES_IRQ && type != SYS_RES_DRQ) 76647398Sdfr return EINVAL; 76750769Sdfr if (rid < 0) 76847398Sdfr return EINVAL; 76950769Sdfr if (type == SYS_RES_IOPORT && rid >= ISA_NPORT) 77050769Sdfr return EINVAL; 77150769Sdfr if (type == SYS_RES_MEMORY && rid >= ISA_NMEM) 77250769Sdfr return EINVAL; 77350769Sdfr if (type == SYS_RES_IRQ && rid >= ISA_NIRQ) 77450769Sdfr return EINVAL; 77550769Sdfr if (type == SYS_RES_DRQ && rid >= ISA_NDRQ) 77650769Sdfr return EINVAL; 77747398Sdfr 77847398Sdfr resource_list_add(rl, type, rid, start, start + count - 1, count); 77947398Sdfr 78047398Sdfr return 0; 78147398Sdfr} 78247398Sdfr 78347398Sdfrstatic int 78447398Sdfrisa_get_resource(device_t dev, device_t child, int type, int rid, 78547398Sdfr u_long *startp, u_long *countp) 78647398Sdfr{ 78747398Sdfr struct isa_device* idev = DEVTOISA(child); 78847398Sdfr struct resource_list *rl = &idev->id_resources; 78947398Sdfr struct resource_list_entry *rle; 79047398Sdfr 79147398Sdfr rle = resource_list_find(rl, type, rid); 79247398Sdfr if (!rle) 79347398Sdfr return ENOENT; 79447398Sdfr 79547398Sdfr *startp = rle->start; 79647398Sdfr *countp = rle->count; 79747398Sdfr 79847398Sdfr return 0; 79947398Sdfr} 80047398Sdfr 80147613Sdfrstatic void 80247613Sdfrisa_delete_resource(device_t dev, device_t child, int type, int rid) 80347613Sdfr{ 80447613Sdfr struct isa_device* idev = DEVTOISA(child); 80547613Sdfr struct resource_list *rl = &idev->id_resources; 80647613Sdfr resource_list_delete(rl, type, rid); 80747613Sdfr} 80847613Sdfr 80950769Sdfrstatic int 81050769Sdfrisa_add_config(device_t dev, device_t child, 81150769Sdfr int priority, struct isa_config *config) 81250769Sdfr{ 81350769Sdfr struct isa_device* idev = DEVTOISA(child); 81450769Sdfr struct isa_config_entry *newice, *ice; 81550769Sdfr 81650769Sdfr newice = malloc(sizeof *ice, M_DEVBUF, M_NOWAIT); 81750769Sdfr if (!newice) 81850769Sdfr return ENOMEM; 81950769Sdfr 82050769Sdfr newice->ice_priority = priority; 82150769Sdfr newice->ice_config = *config; 82250769Sdfr 82350769Sdfr TAILQ_FOREACH(ice, &idev->id_configs, ice_link) { 82450769Sdfr if (ice->ice_priority > priority) 82550769Sdfr break; 82650769Sdfr } 82750769Sdfr if (ice) 82850769Sdfr TAILQ_INSERT_BEFORE(ice, newice, ice_link); 82950769Sdfr else 83050769Sdfr TAILQ_INSERT_TAIL(&idev->id_configs, newice, ice_link); 83150769Sdfr 83250769Sdfr return 0; 83350769Sdfr} 83450769Sdfr 83550769Sdfrstatic void 83650769Sdfrisa_set_config_callback(device_t dev, device_t child, 83750769Sdfr isa_config_cb *fn, void *arg) 83850769Sdfr{ 83950769Sdfr struct isa_device* idev = DEVTOISA(child); 84050769Sdfr 84150769Sdfr idev->id_config_cb = fn; 84250769Sdfr idev->id_config_arg = arg; 84350769Sdfr} 84450769Sdfr 84550769Sdfrstatic int 84650769Sdfrisa_pnp_probe(device_t dev, device_t child, struct isa_pnp_id *ids) 84750769Sdfr{ 84850769Sdfr struct isa_device* idev = DEVTOISA(child); 84950769Sdfr 85050769Sdfr if (!idev->id_vendorid) 85150769Sdfr return ENOENT; 85250769Sdfr 85350769Sdfr while (ids->ip_id) { 85450769Sdfr /* 85550769Sdfr * Really ought to support >1 compat id per device. 85650769Sdfr */ 85750769Sdfr if (idev->id_logicalid == ids->ip_id 85850769Sdfr || idev->id_compatid == ids->ip_id) { 85950910Sdfr if (ids->ip_desc) 86050910Sdfr device_set_desc(child, ids->ip_desc); 86150769Sdfr return 0; 86250769Sdfr } 86350769Sdfr ids++; 86450769Sdfr } 86550769Sdfr 86650769Sdfr return ENXIO; 86750769Sdfr} 86850769Sdfr 86947398Sdfrstatic device_method_t isa_methods[] = { 87047398Sdfr /* Device interface */ 87147398Sdfr DEVMETHOD(device_probe, isa_probe), 87247398Sdfr DEVMETHOD(device_attach, isa_attach), 87347398Sdfr DEVMETHOD(device_detach, bus_generic_detach), 87447398Sdfr DEVMETHOD(device_shutdown, bus_generic_shutdown), 87547398Sdfr DEVMETHOD(device_suspend, bus_generic_suspend), 87647398Sdfr DEVMETHOD(device_resume, bus_generic_resume), 87747398Sdfr 87847398Sdfr /* Bus interface */ 87947398Sdfr DEVMETHOD(bus_add_child, isa_add_child), 88047398Sdfr DEVMETHOD(bus_print_child, isa_print_child), 88147398Sdfr DEVMETHOD(bus_read_ivar, isa_read_ivar), 88247398Sdfr DEVMETHOD(bus_write_ivar, isa_write_ivar), 88350769Sdfr DEVMETHOD(bus_child_detached, isa_child_detached), 88447398Sdfr DEVMETHOD(bus_alloc_resource, isa_alloc_resource), 88547398Sdfr DEVMETHOD(bus_release_resource, isa_release_resource), 88647398Sdfr DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 88747398Sdfr DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 88847398Sdfr DEVMETHOD(bus_setup_intr, isa_setup_intr), 88947398Sdfr DEVMETHOD(bus_teardown_intr, isa_teardown_intr), 89047398Sdfr 89147398Sdfr /* ISA interface */ 89247398Sdfr DEVMETHOD(isa_set_resource, isa_set_resource), 89347398Sdfr DEVMETHOD(isa_get_resource, isa_get_resource), 89447613Sdfr DEVMETHOD(isa_delete_resource, isa_delete_resource), 89550769Sdfr DEVMETHOD(isa_add_config, isa_add_config), 89650769Sdfr DEVMETHOD(isa_set_config_callback, isa_set_config_callback), 89750769Sdfr DEVMETHOD(isa_pnp_probe, isa_pnp_probe), 89847398Sdfr 89947398Sdfr { 0, 0 } 90047398Sdfr}; 90147398Sdfr 90247398Sdfrstatic driver_t isa_driver = { 90347398Sdfr "isa", 90447398Sdfr isa_methods, 90547398Sdfr 1, /* no softc */ 90647398Sdfr}; 90747398Sdfr 90847398Sdfr/* 90947398Sdfr * ISA can be attached to a PCI-ISA bridge or directly to the nexus. 91047398Sdfr */ 91147398SdfrDRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0); 91247398Sdfr#ifdef __i386__ 91347398SdfrDRIVER_MODULE(isa, nexus, isa_driver, isa_devclass, 0, 0); 91447398Sdfr#endif 91550769Sdfr 91650769Sdfr/* 91750769Sdfr * A fallback driver for reporting un-matched pnp devices. 91850769Sdfr */ 91950769Sdfr 92050769Sdfrstatic int 92150769Sdfrunknown_probe(device_t dev) 92250769Sdfr{ 92350769Sdfr /* 92450769Sdfr * Only match pnp devices. 92550769Sdfr */ 92650769Sdfr if (isa_get_vendorid(dev) != 0) 92750769Sdfr return -100; 92850769Sdfr return ENXIO; 92950769Sdfr} 93050769Sdfr 93150769Sdfrstatic int 93250769Sdfrunknown_attach(device_t dev) 93350769Sdfr{ 93450769Sdfr return 0; 93550769Sdfr} 93650769Sdfr 93750769Sdfrstatic int 93850769Sdfrunknown_detach(device_t dev) 93950769Sdfr{ 94050769Sdfr return 0; 94150769Sdfr} 94250769Sdfr 94350769Sdfrstatic device_method_t unknown_methods[] = { 94450769Sdfr /* Device interface */ 94550769Sdfr DEVMETHOD(device_probe, unknown_probe), 94650769Sdfr DEVMETHOD(device_attach, unknown_attach), 94750769Sdfr DEVMETHOD(device_detach, unknown_detach), 94850769Sdfr 94950769Sdfr { 0, 0 } 95050769Sdfr}; 95150769Sdfr 95250769Sdfrstatic driver_t unknown_driver = { 95350769Sdfr "unknown", 95450769Sdfr unknown_methods, 95550769Sdfr 1, /* no softc */ 95650769Sdfr}; 95750769Sdfr 95850769Sdfrstatic devclass_t unknown_devclass; 95950769Sdfr 96050769SdfrDRIVER_MODULE(unknown, isa, unknown_driver, unknown_devclass, 0, 0); 961