isa_common.c revision 52174
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 52174 1999-10-12 21:35:51Z 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 int success, i; 11950769Sdfr struct resource *res[ISA_NMEM]; 12050769Sdfr 12150769Sdfr /* 12250769Sdfr * First clear out any existing resource definitions. 12350769Sdfr */ 12450769Sdfr for (i = 0; i < ISA_NMEM; i++) { 12552174Sdfr bus_delete_resource(child, SYS_RES_MEMORY, i); 12650769Sdfr res[i] = NULL; 12750769Sdfr } 12850769Sdfr 12950769Sdfr success = 1; 13050769Sdfr result->ic_nmem = config->ic_nmem; 13150769Sdfr for (i = 0; i < config->ic_nmem; i++) { 13250769Sdfr u_int32_t start, end, size, align; 13350769Sdfr for (start = config->ic_mem[i].ir_start, 13450769Sdfr end = config->ic_mem[i].ir_end, 13550769Sdfr size = config->ic_mem[i].ir_size, 13650769Sdfr align = config->ic_mem[i].ir_align; 13750769Sdfr start + size - 1 <= end; 13850769Sdfr start += align) { 13952174Sdfr bus_set_resource(child, SYS_RES_MEMORY, i, 14050769Sdfr start, size); 14150769Sdfr res[i] = bus_alloc_resource(child, 14250769Sdfr SYS_RES_MEMORY, &i, 14350769Sdfr 0, ~0, 1, RF_ACTIVE); 14450769Sdfr if (res[i]) { 14550769Sdfr result->ic_mem[i].ir_start = start; 14650769Sdfr result->ic_mem[i].ir_end = start + size - 1; 14750769Sdfr result->ic_mem[i].ir_size = size; 14850769Sdfr result->ic_mem[i].ir_align = align; 14950769Sdfr break; 15050769Sdfr } 15150769Sdfr } 15250769Sdfr 15350769Sdfr /* 15450769Sdfr * If we didn't find a place for memory range i, then 15550769Sdfr * give up now. 15650769Sdfr */ 15750769Sdfr if (!res[i]) { 15850769Sdfr success = 0; 15950769Sdfr break; 16050769Sdfr } 16150769Sdfr } 16250769Sdfr 16350769Sdfr for (i = 0; i < ISA_NMEM; i++) { 16450769Sdfr if (res[i]) 16550769Sdfr bus_release_resource(child, SYS_RES_MEMORY, 16650769Sdfr i, res[i]); 16750769Sdfr } 16850769Sdfr 16950769Sdfr return success; 17050769Sdfr} 17150769Sdfr 17250769Sdfr/* 17350769Sdfr * Find a working set of port regions for a child using the ranges 17450769Sdfr * in *config and return the regions in *result. Returns non-zero if 17550769Sdfr * a set of ranges was found. 17650769Sdfr */ 17750769Sdfrstatic int 17850769Sdfrisa_find_port(device_t child, 17950769Sdfr struct isa_config *config, 18050769Sdfr struct isa_config *result) 18150769Sdfr{ 18250769Sdfr int success, i; 18350769Sdfr struct resource *res[ISA_NPORT]; 18450769Sdfr 18550769Sdfr /* 18650769Sdfr * First clear out any existing resource definitions. 18750769Sdfr */ 18850769Sdfr for (i = 0; i < ISA_NPORT; i++) { 18952174Sdfr bus_delete_resource(child, SYS_RES_IOPORT, i); 19050769Sdfr res[i] = NULL; 19150769Sdfr } 19250769Sdfr 19350769Sdfr success = 1; 19450769Sdfr result->ic_nport = config->ic_nport; 19550769Sdfr for (i = 0; i < config->ic_nport; i++) { 19650769Sdfr u_int32_t start, end, size, align; 19750769Sdfr for (start = config->ic_port[i].ir_start, 19850769Sdfr end = config->ic_port[i].ir_end, 19950769Sdfr size = config->ic_port[i].ir_size, 20050769Sdfr align = config->ic_port[i].ir_align; 20150769Sdfr start + size - 1 <= end; 20250769Sdfr start += align) { 20352174Sdfr bus_set_resource(child, SYS_RES_IOPORT, i, 20450769Sdfr start, size); 20550769Sdfr res[i] = bus_alloc_resource(child, 20650769Sdfr SYS_RES_IOPORT, &i, 20750769Sdfr 0, ~0, 1, RF_ACTIVE); 20850769Sdfr if (res[i]) { 20950769Sdfr result->ic_port[i].ir_start = start; 21050769Sdfr result->ic_port[i].ir_end = start + size - 1; 21150769Sdfr result->ic_port[i].ir_size = size; 21250769Sdfr result->ic_port[i].ir_align = align; 21350769Sdfr break; 21450769Sdfr } 21550769Sdfr } 21650769Sdfr 21750769Sdfr /* 21850769Sdfr * If we didn't find a place for port range i, then 21950769Sdfr * give up now. 22050769Sdfr */ 22150769Sdfr if (!res[i]) { 22250769Sdfr success = 0; 22350769Sdfr break; 22450769Sdfr } 22550769Sdfr } 22650769Sdfr 22750769Sdfr for (i = 0; i < ISA_NPORT; i++) { 22850769Sdfr if (res[i]) 22950769Sdfr bus_release_resource(child, SYS_RES_IOPORT, 23050769Sdfr i, res[i]); 23150769Sdfr } 23250769Sdfr 23350769Sdfr return success; 23450769Sdfr} 23550769Sdfr 23650769Sdfr/* 23750769Sdfr * Return the index of the first bit in the mask (or -1 if mask is empty. 23850769Sdfr */ 23950769Sdfrstatic int 24050769Sdfrfind_first_bit(u_int32_t mask) 24150769Sdfr{ 24250769Sdfr return ffs(mask) - 1; 24350769Sdfr} 24450769Sdfr 24550769Sdfr/* 24650769Sdfr * Return the index of the next bit in the mask, or -1 if there are no more. 24750769Sdfr */ 24850769Sdfrstatic int 24950769Sdfrfind_next_bit(u_int32_t mask, int bit) 25050769Sdfr{ 25150769Sdfr bit++; 25250769Sdfr while (bit < 32 && !(mask & (1 << bit))) 25350769Sdfr bit++; 25450769Sdfr if (bit != 32) 25550769Sdfr return bit; 25650769Sdfr return -1; 25750769Sdfr} 25850769Sdfr 25950769Sdfr/* 26050769Sdfr * Find a working set of irqs for a child using the masks in *config 26150769Sdfr * and return the regions in *result. Returns non-zero if a set of 26250769Sdfr * irqs was found. 26350769Sdfr */ 26450769Sdfrstatic int 26550769Sdfrisa_find_irq(device_t child, 26650769Sdfr struct isa_config *config, 26750769Sdfr struct isa_config *result) 26850769Sdfr{ 26950769Sdfr int success, i; 27050769Sdfr struct resource *res[ISA_NIRQ]; 27150769Sdfr 27250769Sdfr /* 27350769Sdfr * First clear out any existing resource definitions. 27450769Sdfr */ 27550769Sdfr for (i = 0; i < ISA_NIRQ; i++) { 27652174Sdfr bus_delete_resource(child, SYS_RES_IRQ, i); 27750769Sdfr res[i] = NULL; 27850769Sdfr } 27950769Sdfr 28050769Sdfr success = 1; 28150769Sdfr result->ic_nirq = config->ic_nirq; 28250769Sdfr for (i = 0; i < config->ic_nirq; i++) { 28350769Sdfr u_int32_t mask = config->ic_irqmask[i]; 28450769Sdfr int irq; 28550769Sdfr for (irq = find_first_bit(mask); 28650769Sdfr irq != -1; 28750769Sdfr irq = find_next_bit(mask, irq)) { 28852174Sdfr bus_set_resource(child, SYS_RES_IRQ, i, 28950769Sdfr irq, 1); 29050769Sdfr res[i] = bus_alloc_resource(child, 29150769Sdfr SYS_RES_IRQ, &i, 29250769Sdfr 0, ~0, 1, RF_ACTIVE); 29350769Sdfr if (res[i]) { 29450769Sdfr result->ic_irqmask[i] = (1 << irq); 29550769Sdfr break; 29650769Sdfr } 29750769Sdfr } 29850769Sdfr 29950769Sdfr /* 30050769Sdfr * If we didn't find a place for irq range i, then 30150769Sdfr * give up now. 30250769Sdfr */ 30350769Sdfr if (!res[i]) { 30450769Sdfr success = 0; 30550769Sdfr break; 30650769Sdfr } 30750769Sdfr } 30850769Sdfr 30950769Sdfr for (i = 0; i < ISA_NIRQ; i++) { 31050769Sdfr if (res[i]) 31150769Sdfr bus_release_resource(child, SYS_RES_IRQ, 31250769Sdfr i, res[i]); 31350769Sdfr } 31450769Sdfr 31550769Sdfr return success; 31650769Sdfr} 31750769Sdfr 31850769Sdfr/* 31950769Sdfr * Find a working set of drqs for a child using the masks in *config 32050769Sdfr * and return the regions in *result. Returns non-zero if a set of 32150769Sdfr * drqs was found. 32250769Sdfr */ 32350769Sdfrstatic int 32450769Sdfrisa_find_drq(device_t child, 32550769Sdfr struct isa_config *config, 32650769Sdfr struct isa_config *result) 32750769Sdfr{ 32850769Sdfr int success, i; 32950769Sdfr struct resource *res[ISA_NDRQ]; 33050769Sdfr 33150769Sdfr /* 33250769Sdfr * First clear out any existing resource definitions. 33350769Sdfr */ 33450769Sdfr for (i = 0; i < ISA_NDRQ; i++) { 33552174Sdfr bus_delete_resource(child, SYS_RES_DRQ, i); 33650769Sdfr res[i] = NULL; 33750769Sdfr } 33850769Sdfr 33950769Sdfr success = 1; 34050769Sdfr result->ic_ndrq = config->ic_ndrq; 34150769Sdfr for (i = 0; i < config->ic_ndrq; i++) { 34250769Sdfr u_int32_t mask = config->ic_drqmask[i]; 34350769Sdfr int drq; 34450769Sdfr for (drq = find_first_bit(mask); 34550769Sdfr drq != -1; 34650769Sdfr drq = find_next_bit(mask, drq)) { 34752174Sdfr bus_set_resource(child, SYS_RES_DRQ, i, 34850769Sdfr drq, 1); 34950769Sdfr res[i] = bus_alloc_resource(child, 35050769Sdfr SYS_RES_DRQ, &i, 35150769Sdfr 0, ~0, 1, RF_ACTIVE); 35250769Sdfr if (res[i]) { 35350769Sdfr result->ic_drqmask[i] = (1 << drq); 35450769Sdfr break; 35550769Sdfr } 35650769Sdfr } 35750769Sdfr 35850769Sdfr /* 35950769Sdfr * If we didn't find a place for drq range i, then 36050769Sdfr * give up now. 36150769Sdfr */ 36250769Sdfr if (!res[i]) { 36350769Sdfr success = 0; 36450769Sdfr break; 36550769Sdfr } 36650769Sdfr } 36750769Sdfr 36850769Sdfr for (i = 0; i < ISA_NDRQ; i++) { 36950769Sdfr if (res[i]) 37050769Sdfr bus_release_resource(child, SYS_RES_DRQ, 37150769Sdfr i, res[i]); 37250769Sdfr } 37350769Sdfr 37450769Sdfr return success; 37550769Sdfr} 37650769Sdfr 37750769Sdfr/* 37850769Sdfr * Attempt to find a working set of resources for a device. Return 37950769Sdfr * non-zero if a working configuration is found. 38050769Sdfr */ 38150769Sdfrstatic int 38250769Sdfrisa_assign_resources(device_t child) 38350769Sdfr{ 38450769Sdfr struct isa_device *idev = DEVTOISA(child); 38550769Sdfr struct isa_config_entry *ice; 38650769Sdfr struct isa_config config; 38750769Sdfr 38850769Sdfr bzero(&config, sizeof config); 38950769Sdfr TAILQ_FOREACH(ice, &idev->id_configs, ice_link) { 39050769Sdfr if (!isa_find_memory(child, &ice->ice_config, &config)) 39150769Sdfr continue; 39250769Sdfr if (!isa_find_port(child, &ice->ice_config, &config)) 39350769Sdfr continue; 39450769Sdfr if (!isa_find_irq(child, &ice->ice_config, &config)) 39550769Sdfr continue; 39650769Sdfr if (!isa_find_drq(child, &ice->ice_config, &config)) 39750769Sdfr continue; 39850769Sdfr 39950769Sdfr /* 40050769Sdfr * A working configuration was found enable the device 40150769Sdfr * with this configuration. 40250769Sdfr */ 40350769Sdfr if (idev->id_config_cb) { 40450769Sdfr idev->id_config_cb(idev->id_config_arg, 40550769Sdfr &config, 1); 40650769Sdfr return 1; 40750769Sdfr } 40850769Sdfr } 40950769Sdfr 41050769Sdfr /* 41150769Sdfr * Disable the device. 41250769Sdfr */ 41352174Sdfr if (device_get_desc(child)) 41452174Sdfr device_printf(child, "<%s> can't assign resources\n", 41552174Sdfr device_get_desc(child)); 41652174Sdfr else 41752174Sdfr device_printf(child, "can't assign resources\n"); 41850769Sdfr bzero(&config, sizeof config); 41950769Sdfr if (idev->id_config_cb) 42050769Sdfr idev->id_config_cb(idev->id_config_arg, &config, 0); 42150769Sdfr device_disable(child); 42250769Sdfr 42350769Sdfr return 0; 42450769Sdfr} 42550769Sdfr 42650769Sdfr/* 42750769Sdfr * Called after other devices have initialised to probe for isa devices. 42850769Sdfr */ 42950769Sdfrvoid 43050769Sdfrisa_probe_children(device_t dev) 43150769Sdfr{ 43250769Sdfr device_t *children; 43350769Sdfr int nchildren, i; 43450769Sdfr 43550769Sdfr if (device_get_children(dev, &children, &nchildren)) 43650769Sdfr return; 43750769Sdfr 43850769Sdfr /* 43951905Sdfr * First disable all pnp devices so that they don't get 44051905Sdfr * matched by legacy probes. 44151905Sdfr */ 44251905Sdfr for (i = 0; i < nchildren; i++) { 44351905Sdfr device_t child = children[i]; 44451905Sdfr struct isa_device *idev = DEVTOISA(child); 44551905Sdfr struct isa_config config; 44651905Sdfr 44751905Sdfr bzero(&config, sizeof config); 44851905Sdfr if (idev->id_config_cb) 44951905Sdfr idev->id_config_cb(idev->id_config_arg, &config, 0); 45051905Sdfr } 45151905Sdfr 45251905Sdfr /* 45351905Sdfr * Next probe all non-pnp devices so that they claim their 45450769Sdfr * resources first. 45550769Sdfr */ 45650769Sdfr for (i = 0; i < nchildren; i++) { 45750769Sdfr device_t child = children[i]; 45850769Sdfr struct isa_device *idev = DEVTOISA(child); 45950769Sdfr 46050769Sdfr if (TAILQ_FIRST(&idev->id_configs)) 46150769Sdfr continue; 46250769Sdfr 46350769Sdfr device_probe_and_attach(child); 46450769Sdfr } 46550769Sdfr 46650769Sdfr /* 46751905Sdfr * Finally assign resource to pnp devices and probe them. 46850769Sdfr */ 46950769Sdfr for (i = 0; i < nchildren; i++) { 47050769Sdfr device_t child = children[i]; 47150769Sdfr struct isa_device* idev = DEVTOISA(child); 47250769Sdfr 47350769Sdfr if (!TAILQ_FIRST(&idev->id_configs)) 47450769Sdfr continue; 47550769Sdfr 47650769Sdfr if (isa_assign_resources(child)) { 47752174Sdfr struct resource_list *rl = &idev->id_resources; 47850769Sdfr struct resource_list_entry *rle; 47950769Sdfr 48050769Sdfr device_probe_and_attach(child); 48150769Sdfr 48250769Sdfr /* 48350769Sdfr * Claim any unallocated resources to keep other 48450769Sdfr * devices from using them. 48550769Sdfr */ 48652174Sdfr SLIST_FOREACH(rle, rl, link) { 48750769Sdfr if (!rle->res) { 48850769Sdfr int rid = rle->rid; 48952174Sdfr resource_list_alloc(rl, dev, child, 49050769Sdfr rle->type, 49150769Sdfr &rid, 49250769Sdfr 0, ~0, 1, 49350769Sdfr RF_ACTIVE); 49450769Sdfr } 49550769Sdfr } 49650769Sdfr } 49750769Sdfr } 49850769Sdfr 49950769Sdfr free(children, M_TEMP); 50050769Sdfr} 50150769Sdfr 50250769Sdfr/* 50347398Sdfr * Add a new child with default ivars. 50447398Sdfr */ 50547398Sdfrstatic device_t 50647578Sdfrisa_add_child(device_t dev, int order, const char *name, int unit) 50747398Sdfr{ 50847398Sdfr struct isa_device *idev; 50947398Sdfr 51047398Sdfr idev = malloc(sizeof(struct isa_device), M_ISADEV, M_NOWAIT); 51147398Sdfr if (!idev) 51247398Sdfr return 0; 51347398Sdfr bzero(idev, sizeof *idev); 51447398Sdfr 51547398Sdfr resource_list_init(&idev->id_resources); 51650769Sdfr TAILQ_INIT(&idev->id_configs); 51747398Sdfr 51847578Sdfr return device_add_child_ordered(dev, order, name, unit, idev); 51947398Sdfr} 52047398Sdfr 52147398Sdfrstatic void 52247398Sdfrisa_print_resources(struct resource_list *rl, const char *name, int type, 52352174Sdfr int count, const char *format) 52447398Sdfr{ 52550769Sdfr struct resource_list_entry *rle; 52650769Sdfr int printed; 52750769Sdfr int i; 52847398Sdfr 52950769Sdfr printed = 0; 53052174Sdfr for (i = 0; i < count; i++) { 53150769Sdfr rle = resource_list_find(rl, type, i); 53250769Sdfr if (rle) { 53350769Sdfr if (printed == 0) 53450769Sdfr printf(" %s ", name); 53550769Sdfr else if (printed > 0) 53647398Sdfr printf(","); 53750769Sdfr printed++; 53850769Sdfr printf(format, rle->start); 53950769Sdfr if (rle->count > 1) { 54047398Sdfr printf("-"); 54150769Sdfr printf(format, rle->start + rle->count - 1); 54247398Sdfr } 54350769Sdfr } else if (i > 3) { 54450769Sdfr /* check the first few regardless */ 54550769Sdfr break; 54647398Sdfr } 54747398Sdfr } 54847398Sdfr} 54947398Sdfr 55049195Smdoddstatic int 55147398Sdfrisa_print_child(device_t bus, device_t dev) 55247398Sdfr{ 55347398Sdfr struct isa_device *idev = DEVTOISA(dev); 55447398Sdfr struct resource_list *rl = &idev->id_resources; 55549195Smdodd int retval = 0; 55647398Sdfr 55749195Smdodd retval += bus_print_child_header(bus, dev); 55849195Smdodd 55951052Sdfr if (SLIST_FIRST(rl) || device_get_flags(dev)) 56049195Smdodd retval += printf(" at"); 56147398Sdfr 56252174Sdfr isa_print_resources(rl, "port", SYS_RES_IOPORT, ISA_NPORT, "%#lx"); 56352174Sdfr isa_print_resources(rl, "iomem", SYS_RES_MEMORY, ISA_NMEM, "%#lx"); 56452174Sdfr isa_print_resources(rl, "irq", SYS_RES_IRQ, ISA_NIRQ, "%ld"); 56552174Sdfr isa_print_resources(rl, "drq", SYS_RES_DRQ, ISA_NDRQ, "%ld"); 56651052Sdfr if (device_get_flags(dev)) 56751052Sdfr retval += printf(" flags %#x", device_get_flags(dev)); 56847398Sdfr 56949195Smdodd retval += bus_print_child_footer(bus, dev); 57049195Smdodd 57149195Smdodd return (retval); 57247398Sdfr} 57347398Sdfr 57447398Sdfrstatic int 57547398Sdfrisa_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result) 57647398Sdfr{ 57747398Sdfr struct isa_device* idev = DEVTOISA(dev); 57847398Sdfr struct resource_list *rl = &idev->id_resources; 57947398Sdfr struct resource_list_entry *rle; 58047398Sdfr 58147398Sdfr switch (index) { 58247398Sdfr case ISA_IVAR_PORT_0: 58347398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 0); 58447398Sdfr if (rle) 58547398Sdfr *result = rle->start; 58647398Sdfr else 58747398Sdfr *result = -1; 58847398Sdfr break; 58947398Sdfr 59047398Sdfr case ISA_IVAR_PORT_1: 59147398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 1); 59247398Sdfr if (rle) 59347398Sdfr *result = rle->start; 59447398Sdfr else 59547398Sdfr *result = -1; 59647398Sdfr break; 59747398Sdfr 59847398Sdfr case ISA_IVAR_PORTSIZE_0: 59947398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 0); 60047398Sdfr if (rle) 60147398Sdfr *result = rle->count; 60247398Sdfr else 60347398Sdfr *result = 0; 60447398Sdfr break; 60547398Sdfr 60647398Sdfr case ISA_IVAR_PORTSIZE_1: 60747398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 1); 60847398Sdfr if (rle) 60947398Sdfr *result = rle->count; 61047398Sdfr else 61147398Sdfr *result = 0; 61247398Sdfr break; 61347398Sdfr 61447398Sdfr case ISA_IVAR_MADDR_0: 61547398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 0); 61647398Sdfr if (rle) 61747398Sdfr *result = rle->start; 61847398Sdfr else 61947398Sdfr *result = -1; 62047398Sdfr break; 62147398Sdfr 62247398Sdfr case ISA_IVAR_MADDR_1: 62347398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 1); 62447398Sdfr if (rle) 62547398Sdfr *result = rle->start; 62647398Sdfr else 62747398Sdfr *result = -1; 62847398Sdfr break; 62947398Sdfr 63047398Sdfr case ISA_IVAR_MSIZE_0: 63147398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 0); 63247398Sdfr if (rle) 63347398Sdfr *result = rle->count; 63447398Sdfr else 63547398Sdfr *result = 0; 63647398Sdfr break; 63747398Sdfr 63847398Sdfr case ISA_IVAR_MSIZE_1: 63947398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 1); 64047398Sdfr if (rle) 64147398Sdfr *result = rle->count; 64247398Sdfr else 64347398Sdfr *result = 0; 64447398Sdfr break; 64547398Sdfr 64647398Sdfr case ISA_IVAR_IRQ_0: 64747398Sdfr rle = resource_list_find(rl, SYS_RES_IRQ, 0); 64847398Sdfr if (rle) 64947398Sdfr *result = rle->start; 65047398Sdfr else 65147398Sdfr *result = -1; 65247398Sdfr break; 65347398Sdfr 65447398Sdfr case ISA_IVAR_IRQ_1: 65547398Sdfr rle = resource_list_find(rl, SYS_RES_IRQ, 1); 65647398Sdfr if (rle) 65747398Sdfr *result = rle->start; 65847398Sdfr else 65947398Sdfr *result = -1; 66047398Sdfr break; 66147398Sdfr 66247398Sdfr case ISA_IVAR_DRQ_0: 66347398Sdfr rle = resource_list_find(rl, SYS_RES_DRQ, 0); 66447398Sdfr if (rle) 66547398Sdfr *result = rle->start; 66647398Sdfr else 66747398Sdfr *result = -1; 66847398Sdfr break; 66947398Sdfr 67047398Sdfr case ISA_IVAR_DRQ_1: 67147398Sdfr rle = resource_list_find(rl, SYS_RES_DRQ, 1); 67247398Sdfr if (rle) 67347398Sdfr *result = rle->start; 67447398Sdfr else 67547398Sdfr *result = -1; 67647398Sdfr break; 67747398Sdfr 67847613Sdfr case ISA_IVAR_VENDORID: 67947613Sdfr *result = idev->id_vendorid; 68047613Sdfr break; 68147613Sdfr 68247613Sdfr case ISA_IVAR_SERIAL: 68347613Sdfr *result = idev->id_serial; 68447613Sdfr break; 68547613Sdfr 68647613Sdfr case ISA_IVAR_LOGICALID: 68747613Sdfr *result = idev->id_logicalid; 68847613Sdfr break; 68947613Sdfr 69047613Sdfr case ISA_IVAR_COMPATID: 69147613Sdfr *result = idev->id_compatid; 69247613Sdfr break; 69347613Sdfr 69447613Sdfr default: 69547613Sdfr return ENOENT; 69647398Sdfr } 69747613Sdfr 69847613Sdfr return 0; 69947398Sdfr} 70047398Sdfr 70147398Sdfrstatic int 70247398Sdfrisa_write_ivar(device_t bus, device_t dev, 70347398Sdfr int index, uintptr_t value) 70447398Sdfr{ 70547398Sdfr struct isa_device* idev = DEVTOISA(dev); 70647398Sdfr 70747398Sdfr switch (index) { 70847398Sdfr case ISA_IVAR_PORT_0: 70947398Sdfr case ISA_IVAR_PORT_1: 71047398Sdfr case ISA_IVAR_PORTSIZE_0: 71147398Sdfr case ISA_IVAR_PORTSIZE_1: 71247398Sdfr case ISA_IVAR_MADDR_0: 71347398Sdfr case ISA_IVAR_MADDR_1: 71447398Sdfr case ISA_IVAR_MSIZE_0: 71547398Sdfr case ISA_IVAR_MSIZE_1: 71647398Sdfr case ISA_IVAR_IRQ_0: 71747398Sdfr case ISA_IVAR_IRQ_1: 71847398Sdfr case ISA_IVAR_DRQ_0: 71947398Sdfr case ISA_IVAR_DRQ_1: 72047398Sdfr return EINVAL; 72147398Sdfr 72247613Sdfr case ISA_IVAR_VENDORID: 72347613Sdfr idev->id_vendorid = value; 72447613Sdfr break; 72547613Sdfr 72647613Sdfr case ISA_IVAR_SERIAL: 72747613Sdfr idev->id_serial = value; 72847613Sdfr break; 72947613Sdfr 73047613Sdfr case ISA_IVAR_LOGICALID: 73147613Sdfr idev->id_logicalid = value; 73247613Sdfr break; 73347613Sdfr 73447613Sdfr case ISA_IVAR_COMPATID: 73547613Sdfr idev->id_compatid = value; 73647613Sdfr break; 73747613Sdfr 73847398Sdfr default: 73947398Sdfr return (ENOENT); 74047398Sdfr } 74147613Sdfr 74247398Sdfr return (0); 74347398Sdfr} 74447398Sdfr 74550769Sdfr/* 74650769Sdfr * Free any resources which the driver missed or which we were holding for 74750769Sdfr * it (see isa_probe_children). 74850769Sdfr */ 74950769Sdfrstatic void 75050769Sdfrisa_child_detached(device_t dev, device_t child) 75150769Sdfr{ 75250769Sdfr struct isa_device* idev = DEVTOISA(child); 75352174Sdfr struct resource_list *rl = &idev->id_resources; 75450769Sdfr struct resource_list_entry *rle; 75550769Sdfr 75650769Sdfr SLIST_FOREACH(rle, &idev->id_resources, link) { 75750769Sdfr if (rle->res) 75852174Sdfr resource_list_release(rl, dev, child, 75950769Sdfr rle->type, 76050769Sdfr rle->rid, 76150769Sdfr rle->res); 76250769Sdfr } 76350769Sdfr} 76450769Sdfr 76547398Sdfrstatic int 76647398Sdfrisa_set_resource(device_t dev, device_t child, int type, int rid, 76747398Sdfr u_long start, u_long count) 76847398Sdfr{ 76947398Sdfr struct isa_device* idev = DEVTOISA(child); 77047398Sdfr struct resource_list *rl = &idev->id_resources; 77147398Sdfr 77247398Sdfr if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY 77347398Sdfr && type != SYS_RES_IRQ && type != SYS_RES_DRQ) 77447398Sdfr return EINVAL; 77550769Sdfr if (rid < 0) 77647398Sdfr return EINVAL; 77750769Sdfr if (type == SYS_RES_IOPORT && rid >= ISA_NPORT) 77850769Sdfr return EINVAL; 77950769Sdfr if (type == SYS_RES_MEMORY && rid >= ISA_NMEM) 78050769Sdfr return EINVAL; 78150769Sdfr if (type == SYS_RES_IRQ && rid >= ISA_NIRQ) 78250769Sdfr return EINVAL; 78350769Sdfr if (type == SYS_RES_DRQ && rid >= ISA_NDRQ) 78450769Sdfr return EINVAL; 78547398Sdfr 78647398Sdfr resource_list_add(rl, type, rid, start, start + count - 1, count); 78747398Sdfr 78847398Sdfr return 0; 78947398Sdfr} 79047398Sdfr 79147398Sdfrstatic int 79247398Sdfrisa_get_resource(device_t dev, device_t child, int type, int rid, 79347398Sdfr u_long *startp, u_long *countp) 79447398Sdfr{ 79547398Sdfr struct isa_device* idev = DEVTOISA(child); 79647398Sdfr struct resource_list *rl = &idev->id_resources; 79747398Sdfr struct resource_list_entry *rle; 79847398Sdfr 79947398Sdfr rle = resource_list_find(rl, type, rid); 80047398Sdfr if (!rle) 80147398Sdfr return ENOENT; 80247398Sdfr 80347398Sdfr *startp = rle->start; 80447398Sdfr *countp = rle->count; 80547398Sdfr 80647398Sdfr return 0; 80747398Sdfr} 80847398Sdfr 80947613Sdfrstatic void 81047613Sdfrisa_delete_resource(device_t dev, device_t child, int type, int rid) 81147613Sdfr{ 81247613Sdfr struct isa_device* idev = DEVTOISA(child); 81347613Sdfr struct resource_list *rl = &idev->id_resources; 81447613Sdfr resource_list_delete(rl, type, rid); 81547613Sdfr} 81647613Sdfr 81750769Sdfrstatic int 81850769Sdfrisa_add_config(device_t dev, device_t child, 81950769Sdfr int priority, struct isa_config *config) 82050769Sdfr{ 82150769Sdfr struct isa_device* idev = DEVTOISA(child); 82250769Sdfr struct isa_config_entry *newice, *ice; 82350769Sdfr 82450769Sdfr newice = malloc(sizeof *ice, M_DEVBUF, M_NOWAIT); 82550769Sdfr if (!newice) 82650769Sdfr return ENOMEM; 82750769Sdfr 82850769Sdfr newice->ice_priority = priority; 82950769Sdfr newice->ice_config = *config; 83050769Sdfr 83150769Sdfr TAILQ_FOREACH(ice, &idev->id_configs, ice_link) { 83250769Sdfr if (ice->ice_priority > priority) 83350769Sdfr break; 83450769Sdfr } 83550769Sdfr if (ice) 83650769Sdfr TAILQ_INSERT_BEFORE(ice, newice, ice_link); 83750769Sdfr else 83850769Sdfr TAILQ_INSERT_TAIL(&idev->id_configs, newice, ice_link); 83950769Sdfr 84050769Sdfr return 0; 84150769Sdfr} 84250769Sdfr 84350769Sdfrstatic void 84450769Sdfrisa_set_config_callback(device_t dev, device_t child, 84550769Sdfr isa_config_cb *fn, void *arg) 84650769Sdfr{ 84750769Sdfr struct isa_device* idev = DEVTOISA(child); 84850769Sdfr 84950769Sdfr idev->id_config_cb = fn; 85050769Sdfr idev->id_config_arg = arg; 85150769Sdfr} 85250769Sdfr 85350769Sdfrstatic int 85450769Sdfrisa_pnp_probe(device_t dev, device_t child, struct isa_pnp_id *ids) 85550769Sdfr{ 85650769Sdfr struct isa_device* idev = DEVTOISA(child); 85750769Sdfr 85850769Sdfr if (!idev->id_vendorid) 85950769Sdfr return ENOENT; 86050769Sdfr 86150769Sdfr while (ids->ip_id) { 86250769Sdfr /* 86350769Sdfr * Really ought to support >1 compat id per device. 86450769Sdfr */ 86550769Sdfr if (idev->id_logicalid == ids->ip_id 86650769Sdfr || idev->id_compatid == ids->ip_id) { 86750910Sdfr if (ids->ip_desc) 86850910Sdfr device_set_desc(child, ids->ip_desc); 86950769Sdfr return 0; 87050769Sdfr } 87150769Sdfr ids++; 87250769Sdfr } 87350769Sdfr 87450769Sdfr return ENXIO; 87550769Sdfr} 87650769Sdfr 87747398Sdfrstatic device_method_t isa_methods[] = { 87847398Sdfr /* Device interface */ 87947398Sdfr DEVMETHOD(device_probe, isa_probe), 88047398Sdfr DEVMETHOD(device_attach, isa_attach), 88147398Sdfr DEVMETHOD(device_detach, bus_generic_detach), 88247398Sdfr DEVMETHOD(device_shutdown, bus_generic_shutdown), 88347398Sdfr DEVMETHOD(device_suspend, bus_generic_suspend), 88447398Sdfr DEVMETHOD(device_resume, bus_generic_resume), 88547398Sdfr 88647398Sdfr /* Bus interface */ 88747398Sdfr DEVMETHOD(bus_add_child, isa_add_child), 88847398Sdfr DEVMETHOD(bus_print_child, isa_print_child), 88947398Sdfr DEVMETHOD(bus_read_ivar, isa_read_ivar), 89047398Sdfr DEVMETHOD(bus_write_ivar, isa_write_ivar), 89150769Sdfr DEVMETHOD(bus_child_detached, isa_child_detached), 89247398Sdfr DEVMETHOD(bus_alloc_resource, isa_alloc_resource), 89347398Sdfr DEVMETHOD(bus_release_resource, isa_release_resource), 89447398Sdfr DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 89547398Sdfr DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 89647398Sdfr DEVMETHOD(bus_setup_intr, isa_setup_intr), 89747398Sdfr DEVMETHOD(bus_teardown_intr, isa_teardown_intr), 89852174Sdfr DEVMETHOD(bus_set_resource, isa_set_resource), 89952174Sdfr DEVMETHOD(bus_get_resource, isa_get_resource), 90052174Sdfr DEVMETHOD(bus_delete_resource, isa_delete_resource), 90147398Sdfr 90247398Sdfr /* ISA interface */ 90350769Sdfr DEVMETHOD(isa_add_config, isa_add_config), 90450769Sdfr DEVMETHOD(isa_set_config_callback, isa_set_config_callback), 90550769Sdfr DEVMETHOD(isa_pnp_probe, isa_pnp_probe), 90647398Sdfr 90747398Sdfr { 0, 0 } 90847398Sdfr}; 90947398Sdfr 91047398Sdfrstatic driver_t isa_driver = { 91147398Sdfr "isa", 91247398Sdfr isa_methods, 91347398Sdfr 1, /* no softc */ 91447398Sdfr}; 91547398Sdfr 91647398Sdfr/* 91747398Sdfr * ISA can be attached to a PCI-ISA bridge or directly to the nexus. 91847398Sdfr */ 91947398SdfrDRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0); 92047398Sdfr#ifdef __i386__ 92147398SdfrDRIVER_MODULE(isa, nexus, isa_driver, isa_devclass, 0, 0); 92247398Sdfr#endif 92350769Sdfr 92450769Sdfr/* 92550769Sdfr * A fallback driver for reporting un-matched pnp devices. 92650769Sdfr */ 92750769Sdfr 92850769Sdfrstatic int 92950769Sdfrunknown_probe(device_t dev) 93050769Sdfr{ 93150769Sdfr /* 93250769Sdfr * Only match pnp devices. 93350769Sdfr */ 93450769Sdfr if (isa_get_vendorid(dev) != 0) 93550769Sdfr return -100; 93650769Sdfr return ENXIO; 93750769Sdfr} 93850769Sdfr 93950769Sdfrstatic int 94050769Sdfrunknown_attach(device_t dev) 94150769Sdfr{ 94250769Sdfr return 0; 94350769Sdfr} 94450769Sdfr 94550769Sdfrstatic int 94650769Sdfrunknown_detach(device_t dev) 94750769Sdfr{ 94850769Sdfr return 0; 94950769Sdfr} 95050769Sdfr 95150769Sdfrstatic device_method_t unknown_methods[] = { 95250769Sdfr /* Device interface */ 95350769Sdfr DEVMETHOD(device_probe, unknown_probe), 95450769Sdfr DEVMETHOD(device_attach, unknown_attach), 95550769Sdfr DEVMETHOD(device_detach, unknown_detach), 95650769Sdfr 95750769Sdfr { 0, 0 } 95850769Sdfr}; 95950769Sdfr 96050769Sdfrstatic driver_t unknown_driver = { 96150769Sdfr "unknown", 96250769Sdfr unknown_methods, 96350769Sdfr 1, /* no softc */ 96450769Sdfr}; 96550769Sdfr 96650769Sdfrstatic devclass_t unknown_devclass; 96750769Sdfr 96850769SdfrDRIVER_MODULE(unknown, isa, unknown_driver, unknown_devclass, 0, 0); 969