isa_common.c revision 54073
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 54073 1999-12-03 08:41:24Z mdodd $ 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; 8253094Sdfrstatic int isa_running; 8347398Sdfr 8447398Sdfr/* 8547398Sdfr * At 'probe' time, we add all the devices which we know about to the 8647398Sdfr * bus. The generic attach routine will probe and attach them if they 8747398Sdfr * are alive. 8847398Sdfr */ 8947398Sdfrstatic int 9047398Sdfrisa_probe(device_t dev) 9147398Sdfr{ 9247398Sdfr device_set_desc(dev, "ISA bus"); 9347398Sdfr isa_init(); /* Allow machdep code to initialise */ 9453094Sdfr return 0; 9547398Sdfr} 9647398Sdfr 9747398Sdfrextern device_t isa_bus_device; 9847398Sdfr 9947398Sdfrstatic int 10047398Sdfrisa_attach(device_t dev) 10147398Sdfr{ 10247398Sdfr /* 10350769Sdfr * Arrange for isa_probe_children(dev) to be called later. XXX 10447398Sdfr */ 10547398Sdfr isa_bus_device = dev; 10647398Sdfr return 0; 10747398Sdfr} 10847398Sdfr 10947398Sdfr/* 11050769Sdfr * Find a working set of memory regions for a child using the ranges 11150769Sdfr * in *config and return the regions in *result. Returns non-zero if 11250769Sdfr * a set of ranges was found. 11350769Sdfr */ 11450769Sdfrstatic int 11550769Sdfrisa_find_memory(device_t child, 11650769Sdfr struct isa_config *config, 11750769Sdfr struct isa_config *result) 11850769Sdfr{ 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++) { 12652174Sdfr bus_delete_resource(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) { 14052174Sdfr bus_set_resource(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 int success, i; 18450769Sdfr struct resource *res[ISA_NPORT]; 18550769Sdfr 18650769Sdfr /* 18750769Sdfr * First clear out any existing resource definitions. 18850769Sdfr */ 18950769Sdfr for (i = 0; i < ISA_NPORT; i++) { 19052174Sdfr bus_delete_resource(child, SYS_RES_IOPORT, i); 19150769Sdfr res[i] = NULL; 19250769Sdfr } 19350769Sdfr 19450769Sdfr success = 1; 19550769Sdfr result->ic_nport = config->ic_nport; 19650769Sdfr for (i = 0; i < config->ic_nport; i++) { 19750769Sdfr u_int32_t start, end, size, align; 19850769Sdfr for (start = config->ic_port[i].ir_start, 19950769Sdfr end = config->ic_port[i].ir_end, 20050769Sdfr size = config->ic_port[i].ir_size, 20150769Sdfr align = config->ic_port[i].ir_align; 20250769Sdfr start + size - 1 <= end; 20350769Sdfr start += align) { 20452174Sdfr bus_set_resource(child, SYS_RES_IOPORT, i, 20550769Sdfr start, size); 20650769Sdfr res[i] = bus_alloc_resource(child, 20750769Sdfr SYS_RES_IOPORT, &i, 20850769Sdfr 0, ~0, 1, RF_ACTIVE); 20950769Sdfr if (res[i]) { 21050769Sdfr result->ic_port[i].ir_start = start; 21150769Sdfr result->ic_port[i].ir_end = start + size - 1; 21250769Sdfr result->ic_port[i].ir_size = size; 21350769Sdfr result->ic_port[i].ir_align = align; 21450769Sdfr break; 21550769Sdfr } 21650769Sdfr } 21750769Sdfr 21850769Sdfr /* 21950769Sdfr * If we didn't find a place for port range i, then 22050769Sdfr * give up now. 22150769Sdfr */ 22250769Sdfr if (!res[i]) { 22350769Sdfr success = 0; 22450769Sdfr break; 22550769Sdfr } 22650769Sdfr } 22750769Sdfr 22850769Sdfr for (i = 0; i < ISA_NPORT; i++) { 22950769Sdfr if (res[i]) 23050769Sdfr bus_release_resource(child, SYS_RES_IOPORT, 23150769Sdfr i, res[i]); 23250769Sdfr } 23350769Sdfr 23450769Sdfr return success; 23550769Sdfr} 23650769Sdfr 23750769Sdfr/* 23850769Sdfr * Return the index of the first bit in the mask (or -1 if mask is empty. 23950769Sdfr */ 24050769Sdfrstatic int 24150769Sdfrfind_first_bit(u_int32_t mask) 24250769Sdfr{ 24350769Sdfr return ffs(mask) - 1; 24450769Sdfr} 24550769Sdfr 24650769Sdfr/* 24750769Sdfr * Return the index of the next bit in the mask, or -1 if there are no more. 24850769Sdfr */ 24950769Sdfrstatic int 25050769Sdfrfind_next_bit(u_int32_t mask, int bit) 25150769Sdfr{ 25250769Sdfr bit++; 25350769Sdfr while (bit < 32 && !(mask & (1 << bit))) 25450769Sdfr bit++; 25550769Sdfr if (bit != 32) 25650769Sdfr return bit; 25750769Sdfr return -1; 25850769Sdfr} 25950769Sdfr 26050769Sdfr/* 26150769Sdfr * Find a working set of irqs for a child using the masks in *config 26250769Sdfr * and return the regions in *result. Returns non-zero if a set of 26350769Sdfr * irqs was found. 26450769Sdfr */ 26550769Sdfrstatic int 26650769Sdfrisa_find_irq(device_t child, 26750769Sdfr struct isa_config *config, 26850769Sdfr struct isa_config *result) 26950769Sdfr{ 27050769Sdfr int success, i; 27150769Sdfr struct resource *res[ISA_NIRQ]; 27250769Sdfr 27350769Sdfr /* 27450769Sdfr * First clear out any existing resource definitions. 27550769Sdfr */ 27650769Sdfr for (i = 0; i < ISA_NIRQ; i++) { 27752174Sdfr bus_delete_resource(child, SYS_RES_IRQ, i); 27850769Sdfr res[i] = NULL; 27950769Sdfr } 28050769Sdfr 28150769Sdfr success = 1; 28250769Sdfr result->ic_nirq = config->ic_nirq; 28350769Sdfr for (i = 0; i < config->ic_nirq; i++) { 28450769Sdfr u_int32_t mask = config->ic_irqmask[i]; 28550769Sdfr int irq; 28650769Sdfr for (irq = find_first_bit(mask); 28750769Sdfr irq != -1; 28850769Sdfr irq = find_next_bit(mask, irq)) { 28952174Sdfr bus_set_resource(child, SYS_RES_IRQ, i, 29050769Sdfr irq, 1); 29150769Sdfr res[i] = bus_alloc_resource(child, 29250769Sdfr SYS_RES_IRQ, &i, 29350769Sdfr 0, ~0, 1, RF_ACTIVE); 29450769Sdfr if (res[i]) { 29550769Sdfr result->ic_irqmask[i] = (1 << irq); 29650769Sdfr break; 29750769Sdfr } 29850769Sdfr } 29950769Sdfr 30050769Sdfr /* 30150769Sdfr * If we didn't find a place for irq range i, then 30250769Sdfr * give up now. 30350769Sdfr */ 30450769Sdfr if (!res[i]) { 30550769Sdfr success = 0; 30650769Sdfr break; 30750769Sdfr } 30850769Sdfr } 30950769Sdfr 31050769Sdfr for (i = 0; i < ISA_NIRQ; i++) { 31150769Sdfr if (res[i]) 31250769Sdfr bus_release_resource(child, SYS_RES_IRQ, 31350769Sdfr i, res[i]); 31450769Sdfr } 31550769Sdfr 31650769Sdfr return success; 31750769Sdfr} 31850769Sdfr 31950769Sdfr/* 32050769Sdfr * Find a working set of drqs for a child using the masks in *config 32150769Sdfr * and return the regions in *result. Returns non-zero if a set of 32250769Sdfr * drqs was found. 32350769Sdfr */ 32450769Sdfrstatic int 32550769Sdfrisa_find_drq(device_t child, 32650769Sdfr struct isa_config *config, 32750769Sdfr struct isa_config *result) 32850769Sdfr{ 32950769Sdfr int success, i; 33050769Sdfr struct resource *res[ISA_NDRQ]; 33150769Sdfr 33250769Sdfr /* 33350769Sdfr * First clear out any existing resource definitions. 33450769Sdfr */ 33550769Sdfr for (i = 0; i < ISA_NDRQ; i++) { 33652174Sdfr bus_delete_resource(child, SYS_RES_DRQ, i); 33750769Sdfr res[i] = NULL; 33850769Sdfr } 33950769Sdfr 34050769Sdfr success = 1; 34150769Sdfr result->ic_ndrq = config->ic_ndrq; 34250769Sdfr for (i = 0; i < config->ic_ndrq; i++) { 34350769Sdfr u_int32_t mask = config->ic_drqmask[i]; 34450769Sdfr int drq; 34550769Sdfr for (drq = find_first_bit(mask); 34650769Sdfr drq != -1; 34750769Sdfr drq = find_next_bit(mask, drq)) { 34852174Sdfr bus_set_resource(child, SYS_RES_DRQ, i, 34950769Sdfr drq, 1); 35050769Sdfr res[i] = bus_alloc_resource(child, 35150769Sdfr SYS_RES_DRQ, &i, 35250769Sdfr 0, ~0, 1, RF_ACTIVE); 35350769Sdfr if (res[i]) { 35450769Sdfr result->ic_drqmask[i] = (1 << drq); 35550769Sdfr break; 35650769Sdfr } 35750769Sdfr } 35850769Sdfr 35950769Sdfr /* 36050769Sdfr * If we didn't find a place for drq range i, then 36150769Sdfr * give up now. 36250769Sdfr */ 36350769Sdfr if (!res[i]) { 36450769Sdfr success = 0; 36550769Sdfr break; 36650769Sdfr } 36750769Sdfr } 36850769Sdfr 36950769Sdfr for (i = 0; i < ISA_NDRQ; i++) { 37050769Sdfr if (res[i]) 37150769Sdfr bus_release_resource(child, SYS_RES_DRQ, 37250769Sdfr i, res[i]); 37350769Sdfr } 37450769Sdfr 37550769Sdfr return success; 37650769Sdfr} 37750769Sdfr 37850769Sdfr/* 37950769Sdfr * Attempt to find a working set of resources for a device. Return 38050769Sdfr * non-zero if a working configuration is found. 38150769Sdfr */ 38250769Sdfrstatic int 38350769Sdfrisa_assign_resources(device_t child) 38450769Sdfr{ 38550769Sdfr struct isa_device *idev = DEVTOISA(child); 38650769Sdfr struct isa_config_entry *ice; 38750769Sdfr struct isa_config config; 38850769Sdfr 38950769Sdfr bzero(&config, sizeof config); 39050769Sdfr TAILQ_FOREACH(ice, &idev->id_configs, ice_link) { 39150769Sdfr if (!isa_find_memory(child, &ice->ice_config, &config)) 39250769Sdfr continue; 39350769Sdfr if (!isa_find_port(child, &ice->ice_config, &config)) 39450769Sdfr continue; 39550769Sdfr if (!isa_find_irq(child, &ice->ice_config, &config)) 39650769Sdfr continue; 39750769Sdfr if (!isa_find_drq(child, &ice->ice_config, &config)) 39850769Sdfr continue; 39950769Sdfr 40050769Sdfr /* 40150769Sdfr * A working configuration was found enable the device 40250769Sdfr * with this configuration. 40350769Sdfr */ 40450769Sdfr if (idev->id_config_cb) { 40550769Sdfr idev->id_config_cb(idev->id_config_arg, 40650769Sdfr &config, 1); 40750769Sdfr return 1; 40850769Sdfr } 40950769Sdfr } 41050769Sdfr 41150769Sdfr /* 41250769Sdfr * Disable the device. 41350769Sdfr */ 41452174Sdfr if (device_get_desc(child)) 41552174Sdfr device_printf(child, "<%s> can't assign resources\n", 41652174Sdfr device_get_desc(child)); 41752174Sdfr else 41852174Sdfr device_printf(child, "can't assign resources\n"); 41950769Sdfr bzero(&config, sizeof config); 42050769Sdfr if (idev->id_config_cb) 42150769Sdfr idev->id_config_cb(idev->id_config_arg, &config, 0); 42250769Sdfr device_disable(child); 42350769Sdfr 42450769Sdfr return 0; 42550769Sdfr} 42650769Sdfr 42750769Sdfr/* 42850769Sdfr * Called after other devices have initialised to probe for isa devices. 42950769Sdfr */ 43050769Sdfrvoid 43150769Sdfrisa_probe_children(device_t dev) 43250769Sdfr{ 43350769Sdfr device_t *children; 43450769Sdfr int nchildren, i; 43550769Sdfr 43653094Sdfr /* 43753094Sdfr * Create all the children by calling driver's identify methods. 43853094Sdfr */ 43953094Sdfr bus_generic_probe(dev); 44053094Sdfr 44150769Sdfr if (device_get_children(dev, &children, &nchildren)) 44250769Sdfr return; 44350769Sdfr 44450769Sdfr /* 44551905Sdfr * First disable all pnp devices so that they don't get 44651905Sdfr * matched by legacy probes. 44751905Sdfr */ 44853094Sdfr if (bootverbose) 44953094Sdfr printf("isa_probe_children: disabling PnP devices\n"); 45051905Sdfr for (i = 0; i < nchildren; i++) { 45151905Sdfr device_t child = children[i]; 45251905Sdfr struct isa_device *idev = DEVTOISA(child); 45351905Sdfr struct isa_config config; 45451905Sdfr 45551905Sdfr bzero(&config, sizeof config); 45651905Sdfr if (idev->id_config_cb) 45751905Sdfr idev->id_config_cb(idev->id_config_arg, &config, 0); 45851905Sdfr } 45951905Sdfr 46051905Sdfr /* 46151905Sdfr * Next probe all non-pnp devices so that they claim their 46250769Sdfr * resources first. 46350769Sdfr */ 46453094Sdfr if (bootverbose) 46553094Sdfr printf("isa_probe_children: probing non-PnP devices\n"); 46650769Sdfr for (i = 0; i < nchildren; i++) { 46750769Sdfr device_t child = children[i]; 46850769Sdfr struct isa_device *idev = DEVTOISA(child); 46950769Sdfr 47050769Sdfr if (TAILQ_FIRST(&idev->id_configs)) 47150769Sdfr continue; 47250769Sdfr 47350769Sdfr device_probe_and_attach(child); 47450769Sdfr } 47550769Sdfr 47650769Sdfr /* 47751905Sdfr * Finally assign resource to pnp devices and probe them. 47850769Sdfr */ 47953094Sdfr if (bootverbose) 48053094Sdfr printf("isa_probe_children: probing PnP devices\n"); 48150769Sdfr for (i = 0; i < nchildren; i++) { 48250769Sdfr device_t child = children[i]; 48350769Sdfr struct isa_device* idev = DEVTOISA(child); 48450769Sdfr 48550769Sdfr if (!TAILQ_FIRST(&idev->id_configs)) 48650769Sdfr continue; 48750769Sdfr 48850769Sdfr if (isa_assign_resources(child)) { 48952174Sdfr struct resource_list *rl = &idev->id_resources; 49050769Sdfr struct resource_list_entry *rle; 49150769Sdfr 49250769Sdfr device_probe_and_attach(child); 49350769Sdfr 49450769Sdfr /* 49550769Sdfr * Claim any unallocated resources to keep other 49650769Sdfr * devices from using them. 49750769Sdfr */ 49852174Sdfr SLIST_FOREACH(rle, rl, link) { 49950769Sdfr if (!rle->res) { 50050769Sdfr int rid = rle->rid; 50152174Sdfr resource_list_alloc(rl, dev, child, 50250769Sdfr rle->type, 50350769Sdfr &rid, 50450769Sdfr 0, ~0, 1, 50550769Sdfr RF_ACTIVE); 50650769Sdfr } 50750769Sdfr } 50850769Sdfr } 50950769Sdfr } 51050769Sdfr 51150769Sdfr free(children, M_TEMP); 51253094Sdfr 51353094Sdfr isa_running = 1; 51450769Sdfr} 51550769Sdfr 51650769Sdfr/* 51747398Sdfr * Add a new child with default ivars. 51847398Sdfr */ 51947398Sdfrstatic device_t 52047578Sdfrisa_add_child(device_t dev, int order, const char *name, int unit) 52147398Sdfr{ 52254073Smdodd device_t child; 52347398Sdfr struct isa_device *idev; 52447398Sdfr 52547398Sdfr idev = malloc(sizeof(struct isa_device), M_ISADEV, M_NOWAIT); 52647398Sdfr if (!idev) 52747398Sdfr return 0; 52847398Sdfr bzero(idev, sizeof *idev); 52947398Sdfr 53047398Sdfr resource_list_init(&idev->id_resources); 53150769Sdfr TAILQ_INIT(&idev->id_configs); 53247398Sdfr 53354073Smdodd child = device_add_child_ordered(dev, order, name, unit); 53454073Smdodd device_set_ivars(child, idev); 53554073Smdodd 53654073Smdodd return child; 53747398Sdfr} 53847398Sdfr 53947398Sdfrstatic void 54047398Sdfrisa_print_resources(struct resource_list *rl, const char *name, int type, 54152174Sdfr int count, const char *format) 54247398Sdfr{ 54350769Sdfr struct resource_list_entry *rle; 54450769Sdfr int printed; 54550769Sdfr int i; 54647398Sdfr 54750769Sdfr printed = 0; 54852174Sdfr for (i = 0; i < count; i++) { 54950769Sdfr rle = resource_list_find(rl, type, i); 55050769Sdfr if (rle) { 55150769Sdfr if (printed == 0) 55250769Sdfr printf(" %s ", name); 55350769Sdfr else if (printed > 0) 55447398Sdfr printf(","); 55550769Sdfr printed++; 55650769Sdfr printf(format, rle->start); 55750769Sdfr if (rle->count > 1) { 55847398Sdfr printf("-"); 55950769Sdfr printf(format, rle->start + rle->count - 1); 56047398Sdfr } 56150769Sdfr } else if (i > 3) { 56250769Sdfr /* check the first few regardless */ 56350769Sdfr break; 56447398Sdfr } 56547398Sdfr } 56647398Sdfr} 56747398Sdfr 56849195Smdoddstatic int 56947398Sdfrisa_print_child(device_t bus, device_t dev) 57047398Sdfr{ 57147398Sdfr struct isa_device *idev = DEVTOISA(dev); 57247398Sdfr struct resource_list *rl = &idev->id_resources; 57349195Smdodd int retval = 0; 57447398Sdfr 57549195Smdodd retval += bus_print_child_header(bus, dev); 57649195Smdodd 57751052Sdfr if (SLIST_FIRST(rl) || device_get_flags(dev)) 57849195Smdodd retval += printf(" at"); 57947398Sdfr 58052174Sdfr isa_print_resources(rl, "port", SYS_RES_IOPORT, ISA_NPORT, "%#lx"); 58152174Sdfr isa_print_resources(rl, "iomem", SYS_RES_MEMORY, ISA_NMEM, "%#lx"); 58252174Sdfr isa_print_resources(rl, "irq", SYS_RES_IRQ, ISA_NIRQ, "%ld"); 58352174Sdfr isa_print_resources(rl, "drq", SYS_RES_DRQ, ISA_NDRQ, "%ld"); 58451052Sdfr if (device_get_flags(dev)) 58551052Sdfr retval += printf(" flags %#x", device_get_flags(dev)); 58647398Sdfr 58749195Smdodd retval += bus_print_child_footer(bus, dev); 58849195Smdodd 58949195Smdodd return (retval); 59047398Sdfr} 59147398Sdfr 59247398Sdfrstatic int 59347398Sdfrisa_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result) 59447398Sdfr{ 59547398Sdfr struct isa_device* idev = DEVTOISA(dev); 59647398Sdfr struct resource_list *rl = &idev->id_resources; 59747398Sdfr struct resource_list_entry *rle; 59847398Sdfr 59947398Sdfr switch (index) { 60047398Sdfr case ISA_IVAR_PORT_0: 60147398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 0); 60247398Sdfr if (rle) 60347398Sdfr *result = rle->start; 60447398Sdfr else 60547398Sdfr *result = -1; 60647398Sdfr break; 60747398Sdfr 60847398Sdfr case ISA_IVAR_PORT_1: 60947398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 1); 61047398Sdfr if (rle) 61147398Sdfr *result = rle->start; 61247398Sdfr else 61347398Sdfr *result = -1; 61447398Sdfr break; 61547398Sdfr 61647398Sdfr case ISA_IVAR_PORTSIZE_0: 61747398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 0); 61847398Sdfr if (rle) 61947398Sdfr *result = rle->count; 62047398Sdfr else 62147398Sdfr *result = 0; 62247398Sdfr break; 62347398Sdfr 62447398Sdfr case ISA_IVAR_PORTSIZE_1: 62547398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 1); 62647398Sdfr if (rle) 62747398Sdfr *result = rle->count; 62847398Sdfr else 62947398Sdfr *result = 0; 63047398Sdfr break; 63147398Sdfr 63247398Sdfr case ISA_IVAR_MADDR_0: 63347398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 0); 63447398Sdfr if (rle) 63547398Sdfr *result = rle->start; 63647398Sdfr else 63747398Sdfr *result = -1; 63847398Sdfr break; 63947398Sdfr 64047398Sdfr case ISA_IVAR_MADDR_1: 64147398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 1); 64247398Sdfr if (rle) 64347398Sdfr *result = rle->start; 64447398Sdfr else 64547398Sdfr *result = -1; 64647398Sdfr break; 64747398Sdfr 64847398Sdfr case ISA_IVAR_MSIZE_0: 64947398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 0); 65047398Sdfr if (rle) 65147398Sdfr *result = rle->count; 65247398Sdfr else 65347398Sdfr *result = 0; 65447398Sdfr break; 65547398Sdfr 65647398Sdfr case ISA_IVAR_MSIZE_1: 65747398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 1); 65847398Sdfr if (rle) 65947398Sdfr *result = rle->count; 66047398Sdfr else 66147398Sdfr *result = 0; 66247398Sdfr break; 66347398Sdfr 66447398Sdfr case ISA_IVAR_IRQ_0: 66547398Sdfr rle = resource_list_find(rl, SYS_RES_IRQ, 0); 66647398Sdfr if (rle) 66747398Sdfr *result = rle->start; 66847398Sdfr else 66947398Sdfr *result = -1; 67047398Sdfr break; 67147398Sdfr 67247398Sdfr case ISA_IVAR_IRQ_1: 67347398Sdfr rle = resource_list_find(rl, SYS_RES_IRQ, 1); 67447398Sdfr if (rle) 67547398Sdfr *result = rle->start; 67647398Sdfr else 67747398Sdfr *result = -1; 67847398Sdfr break; 67947398Sdfr 68047398Sdfr case ISA_IVAR_DRQ_0: 68147398Sdfr rle = resource_list_find(rl, SYS_RES_DRQ, 0); 68247398Sdfr if (rle) 68347398Sdfr *result = rle->start; 68447398Sdfr else 68547398Sdfr *result = -1; 68647398Sdfr break; 68747398Sdfr 68847398Sdfr case ISA_IVAR_DRQ_1: 68947398Sdfr rle = resource_list_find(rl, SYS_RES_DRQ, 1); 69047398Sdfr if (rle) 69147398Sdfr *result = rle->start; 69247398Sdfr else 69347398Sdfr *result = -1; 69447398Sdfr break; 69547398Sdfr 69647613Sdfr case ISA_IVAR_VENDORID: 69747613Sdfr *result = idev->id_vendorid; 69847613Sdfr break; 69947613Sdfr 70047613Sdfr case ISA_IVAR_SERIAL: 70147613Sdfr *result = idev->id_serial; 70247613Sdfr break; 70347613Sdfr 70447613Sdfr case ISA_IVAR_LOGICALID: 70547613Sdfr *result = idev->id_logicalid; 70647613Sdfr break; 70747613Sdfr 70847613Sdfr case ISA_IVAR_COMPATID: 70947613Sdfr *result = idev->id_compatid; 71047613Sdfr break; 71147613Sdfr 71247613Sdfr default: 71347613Sdfr return ENOENT; 71447398Sdfr } 71547613Sdfr 71647613Sdfr return 0; 71747398Sdfr} 71847398Sdfr 71947398Sdfrstatic int 72047398Sdfrisa_write_ivar(device_t bus, device_t dev, 72147398Sdfr int index, uintptr_t value) 72247398Sdfr{ 72347398Sdfr struct isa_device* idev = DEVTOISA(dev); 72447398Sdfr 72547398Sdfr switch (index) { 72647398Sdfr case ISA_IVAR_PORT_0: 72747398Sdfr case ISA_IVAR_PORT_1: 72847398Sdfr case ISA_IVAR_PORTSIZE_0: 72947398Sdfr case ISA_IVAR_PORTSIZE_1: 73047398Sdfr case ISA_IVAR_MADDR_0: 73147398Sdfr case ISA_IVAR_MADDR_1: 73247398Sdfr case ISA_IVAR_MSIZE_0: 73347398Sdfr case ISA_IVAR_MSIZE_1: 73447398Sdfr case ISA_IVAR_IRQ_0: 73547398Sdfr case ISA_IVAR_IRQ_1: 73647398Sdfr case ISA_IVAR_DRQ_0: 73747398Sdfr case ISA_IVAR_DRQ_1: 73847398Sdfr return EINVAL; 73947398Sdfr 74047613Sdfr case ISA_IVAR_VENDORID: 74147613Sdfr idev->id_vendorid = value; 74247613Sdfr break; 74347613Sdfr 74447613Sdfr case ISA_IVAR_SERIAL: 74547613Sdfr idev->id_serial = value; 74647613Sdfr break; 74747613Sdfr 74847613Sdfr case ISA_IVAR_LOGICALID: 74947613Sdfr idev->id_logicalid = value; 75047613Sdfr break; 75147613Sdfr 75247613Sdfr case ISA_IVAR_COMPATID: 75347613Sdfr idev->id_compatid = value; 75447613Sdfr break; 75547613Sdfr 75647398Sdfr default: 75747398Sdfr return (ENOENT); 75847398Sdfr } 75947613Sdfr 76047398Sdfr return (0); 76147398Sdfr} 76247398Sdfr 76350769Sdfr/* 76450769Sdfr * Free any resources which the driver missed or which we were holding for 76550769Sdfr * it (see isa_probe_children). 76650769Sdfr */ 76750769Sdfrstatic void 76850769Sdfrisa_child_detached(device_t dev, device_t child) 76950769Sdfr{ 77050769Sdfr struct isa_device* idev = DEVTOISA(child); 77152174Sdfr struct resource_list *rl = &idev->id_resources; 77250769Sdfr struct resource_list_entry *rle; 77350769Sdfr 77450769Sdfr SLIST_FOREACH(rle, &idev->id_resources, link) { 77550769Sdfr if (rle->res) 77652174Sdfr resource_list_release(rl, dev, child, 77750769Sdfr rle->type, 77850769Sdfr rle->rid, 77950769Sdfr rle->res); 78050769Sdfr } 78150769Sdfr} 78250769Sdfr 78353094Sdfrstatic void 78453094Sdfrisa_driver_added(device_t dev, driver_t *driver) 78553094Sdfr{ 78653094Sdfr device_t *children; 78753094Sdfr int nchildren, i; 78853094Sdfr 78953094Sdfr /* 79053094Sdfr * Don't do anything if drivers are dynamically 79153094Sdfr * added during autoconfiguration (cf. ymf724). 79253094Sdfr * since that would end up calling identify 79353094Sdfr * twice. 79453094Sdfr */ 79553094Sdfr if (!isa_running) 79653094Sdfr return; 79753094Sdfr 79853094Sdfr DEVICE_IDENTIFY(driver, dev); 79953094Sdfr if (device_get_children(dev, &children, &nchildren)) 80053094Sdfr return; 80153094Sdfr 80253094Sdfr for (i = 0; i < nchildren; i++) { 80353094Sdfr device_t child = children[i]; 80453094Sdfr struct isa_device *idev = DEVTOISA(child); 80553094Sdfr struct resource_list *rl = &idev->id_resources; 80653094Sdfr struct resource_list_entry *rle; 80753094Sdfr 80853094Sdfr if (device_get_state(child) != DS_NOTPRESENT) 80953094Sdfr continue; 81053094Sdfr 81153094Sdfr if (TAILQ_FIRST(&idev->id_configs)) 81253094Sdfr if (!isa_assign_resources(child)) 81353094Sdfr continue; 81453094Sdfr 81553094Sdfr device_probe_and_attach(child); 81653094Sdfr 81753094Sdfr if (TAILQ_FIRST(&idev->id_configs)) { 81853094Sdfr /* 81953094Sdfr * Claim any unallocated resources to keep other 82053094Sdfr * devices from using them. 82153094Sdfr */ 82253094Sdfr SLIST_FOREACH(rle, rl, link) { 82353094Sdfr if (!rle->res) { 82453094Sdfr int rid = rle->rid; 82553094Sdfr resource_list_alloc(rl, dev, child, 82653094Sdfr rle->type, 82753094Sdfr &rid, 82853094Sdfr 0, ~0, 1, 82953094Sdfr RF_ACTIVE); 83053094Sdfr } 83153094Sdfr } 83253094Sdfr } 83353094Sdfr } 83453094Sdfr 83553094Sdfr free(children, M_TEMP); 83653094Sdfr} 83753094Sdfr 83847398Sdfrstatic int 83947398Sdfrisa_set_resource(device_t dev, device_t child, int type, int rid, 84047398Sdfr u_long start, u_long count) 84147398Sdfr{ 84247398Sdfr struct isa_device* idev = DEVTOISA(child); 84347398Sdfr struct resource_list *rl = &idev->id_resources; 84447398Sdfr 84547398Sdfr if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY 84647398Sdfr && type != SYS_RES_IRQ && type != SYS_RES_DRQ) 84747398Sdfr return EINVAL; 84850769Sdfr if (rid < 0) 84947398Sdfr return EINVAL; 85050769Sdfr if (type == SYS_RES_IOPORT && rid >= ISA_NPORT) 85150769Sdfr return EINVAL; 85250769Sdfr if (type == SYS_RES_MEMORY && rid >= ISA_NMEM) 85350769Sdfr return EINVAL; 85450769Sdfr if (type == SYS_RES_IRQ && rid >= ISA_NIRQ) 85550769Sdfr return EINVAL; 85650769Sdfr if (type == SYS_RES_DRQ && rid >= ISA_NDRQ) 85750769Sdfr return EINVAL; 85847398Sdfr 85947398Sdfr resource_list_add(rl, type, rid, start, start + count - 1, count); 86047398Sdfr 86147398Sdfr return 0; 86247398Sdfr} 86347398Sdfr 86447398Sdfrstatic int 86547398Sdfrisa_get_resource(device_t dev, device_t child, int type, int rid, 86647398Sdfr u_long *startp, u_long *countp) 86747398Sdfr{ 86847398Sdfr struct isa_device* idev = DEVTOISA(child); 86947398Sdfr struct resource_list *rl = &idev->id_resources; 87047398Sdfr struct resource_list_entry *rle; 87147398Sdfr 87247398Sdfr rle = resource_list_find(rl, type, rid); 87347398Sdfr if (!rle) 87447398Sdfr return ENOENT; 87547398Sdfr 87653461Speter if (startp) 87753461Speter *startp = rle->start; 87853461Speter if (countp) 87953461Speter *countp = rle->count; 88047398Sdfr 88147398Sdfr return 0; 88247398Sdfr} 88347398Sdfr 88447613Sdfrstatic void 88547613Sdfrisa_delete_resource(device_t dev, device_t child, int type, int rid) 88647613Sdfr{ 88747613Sdfr struct isa_device* idev = DEVTOISA(child); 88847613Sdfr struct resource_list *rl = &idev->id_resources; 88947613Sdfr resource_list_delete(rl, type, rid); 89047613Sdfr} 89147613Sdfr 89250769Sdfrstatic int 89350769Sdfrisa_add_config(device_t dev, device_t child, 89450769Sdfr int priority, struct isa_config *config) 89550769Sdfr{ 89650769Sdfr struct isa_device* idev = DEVTOISA(child); 89750769Sdfr struct isa_config_entry *newice, *ice; 89850769Sdfr 89950769Sdfr newice = malloc(sizeof *ice, M_DEVBUF, M_NOWAIT); 90050769Sdfr if (!newice) 90150769Sdfr return ENOMEM; 90250769Sdfr 90350769Sdfr newice->ice_priority = priority; 90450769Sdfr newice->ice_config = *config; 90550769Sdfr 90650769Sdfr TAILQ_FOREACH(ice, &idev->id_configs, ice_link) { 90750769Sdfr if (ice->ice_priority > priority) 90850769Sdfr break; 90950769Sdfr } 91050769Sdfr if (ice) 91150769Sdfr TAILQ_INSERT_BEFORE(ice, newice, ice_link); 91250769Sdfr else 91350769Sdfr TAILQ_INSERT_TAIL(&idev->id_configs, newice, ice_link); 91450769Sdfr 91550769Sdfr return 0; 91650769Sdfr} 91750769Sdfr 91850769Sdfrstatic void 91950769Sdfrisa_set_config_callback(device_t dev, device_t child, 92050769Sdfr isa_config_cb *fn, void *arg) 92150769Sdfr{ 92250769Sdfr struct isa_device* idev = DEVTOISA(child); 92350769Sdfr 92450769Sdfr idev->id_config_cb = fn; 92550769Sdfr idev->id_config_arg = arg; 92650769Sdfr} 92750769Sdfr 92850769Sdfrstatic int 92950769Sdfrisa_pnp_probe(device_t dev, device_t child, struct isa_pnp_id *ids) 93050769Sdfr{ 93150769Sdfr struct isa_device* idev = DEVTOISA(child); 93250769Sdfr 93350769Sdfr if (!idev->id_vendorid) 93450769Sdfr return ENOENT; 93550769Sdfr 93650769Sdfr while (ids->ip_id) { 93750769Sdfr /* 93850769Sdfr * Really ought to support >1 compat id per device. 93950769Sdfr */ 94050769Sdfr if (idev->id_logicalid == ids->ip_id 94150769Sdfr || idev->id_compatid == ids->ip_id) { 94250910Sdfr if (ids->ip_desc) 94350910Sdfr device_set_desc(child, ids->ip_desc); 94450769Sdfr return 0; 94550769Sdfr } 94650769Sdfr ids++; 94750769Sdfr } 94850769Sdfr 94950769Sdfr return ENXIO; 95050769Sdfr} 95150769Sdfr 95247398Sdfrstatic device_method_t isa_methods[] = { 95347398Sdfr /* Device interface */ 95447398Sdfr DEVMETHOD(device_probe, isa_probe), 95547398Sdfr DEVMETHOD(device_attach, isa_attach), 95647398Sdfr DEVMETHOD(device_detach, bus_generic_detach), 95747398Sdfr DEVMETHOD(device_shutdown, bus_generic_shutdown), 95847398Sdfr DEVMETHOD(device_suspend, bus_generic_suspend), 95947398Sdfr DEVMETHOD(device_resume, bus_generic_resume), 96047398Sdfr 96147398Sdfr /* Bus interface */ 96247398Sdfr DEVMETHOD(bus_add_child, isa_add_child), 96347398Sdfr DEVMETHOD(bus_print_child, isa_print_child), 96447398Sdfr DEVMETHOD(bus_read_ivar, isa_read_ivar), 96547398Sdfr DEVMETHOD(bus_write_ivar, isa_write_ivar), 96650769Sdfr DEVMETHOD(bus_child_detached, isa_child_detached), 96753094Sdfr DEVMETHOD(bus_driver_added, isa_driver_added), 96847398Sdfr DEVMETHOD(bus_alloc_resource, isa_alloc_resource), 96947398Sdfr DEVMETHOD(bus_release_resource, isa_release_resource), 97047398Sdfr DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 97147398Sdfr DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 97247398Sdfr DEVMETHOD(bus_setup_intr, isa_setup_intr), 97347398Sdfr DEVMETHOD(bus_teardown_intr, isa_teardown_intr), 97452174Sdfr DEVMETHOD(bus_set_resource, isa_set_resource), 97552174Sdfr DEVMETHOD(bus_get_resource, isa_get_resource), 97652174Sdfr DEVMETHOD(bus_delete_resource, isa_delete_resource), 97747398Sdfr 97847398Sdfr /* ISA interface */ 97950769Sdfr DEVMETHOD(isa_add_config, isa_add_config), 98050769Sdfr DEVMETHOD(isa_set_config_callback, isa_set_config_callback), 98150769Sdfr DEVMETHOD(isa_pnp_probe, isa_pnp_probe), 98247398Sdfr 98347398Sdfr { 0, 0 } 98447398Sdfr}; 98547398Sdfr 98647398Sdfrstatic driver_t isa_driver = { 98747398Sdfr "isa", 98847398Sdfr isa_methods, 98947398Sdfr 1, /* no softc */ 99047398Sdfr}; 99147398Sdfr 99247398Sdfr/* 99347398Sdfr * ISA can be attached to a PCI-ISA bridge or directly to the nexus. 99447398Sdfr */ 99547398SdfrDRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0); 99647398Sdfr#ifdef __i386__ 99747398SdfrDRIVER_MODULE(isa, nexus, isa_driver, isa_devclass, 0, 0); 99847398Sdfr#endif 99950769Sdfr 100050769Sdfr/* 100150769Sdfr * A fallback driver for reporting un-matched pnp devices. 100250769Sdfr */ 100350769Sdfr 100450769Sdfrstatic int 100550769Sdfrunknown_probe(device_t dev) 100650769Sdfr{ 100750769Sdfr /* 100850769Sdfr * Only match pnp devices. 100950769Sdfr */ 101050769Sdfr if (isa_get_vendorid(dev) != 0) 101150769Sdfr return -100; 101250769Sdfr return ENXIO; 101350769Sdfr} 101450769Sdfr 101550769Sdfrstatic int 101650769Sdfrunknown_attach(device_t dev) 101750769Sdfr{ 101850769Sdfr return 0; 101950769Sdfr} 102050769Sdfr 102150769Sdfrstatic int 102250769Sdfrunknown_detach(device_t dev) 102350769Sdfr{ 102450769Sdfr return 0; 102550769Sdfr} 102650769Sdfr 102750769Sdfrstatic device_method_t unknown_methods[] = { 102850769Sdfr /* Device interface */ 102950769Sdfr DEVMETHOD(device_probe, unknown_probe), 103050769Sdfr DEVMETHOD(device_attach, unknown_attach), 103150769Sdfr DEVMETHOD(device_detach, unknown_detach), 103250769Sdfr 103350769Sdfr { 0, 0 } 103450769Sdfr}; 103550769Sdfr 103650769Sdfrstatic driver_t unknown_driver = { 103750769Sdfr "unknown", 103850769Sdfr unknown_methods, 103950769Sdfr 1, /* no softc */ 104050769Sdfr}; 104150769Sdfr 104250769Sdfrstatic devclass_t unknown_devclass; 104350769Sdfr 104450769SdfrDRIVER_MODULE(unknown, isa, unknown_driver, unknown_devclass, 0, 0); 1045