isa_common.c revision 69774
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 69774 2000-12-08 20:09:00Z phk $ 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 7962987Sjhbstatic int isa_print_child(device_t bus, device_t dev); 8062987Sjhb 8169774Sphkstatic MALLOC_DEFINE(M_ISADEV, "isadev", "ISA device"); 8247398Sdfr 8347398Sdfrstatic devclass_t isa_devclass; 8453094Sdfrstatic int isa_running; 8547398Sdfr 8647398Sdfr/* 8747398Sdfr * At 'probe' time, we add all the devices which we know about to the 8847398Sdfr * bus. The generic attach routine will probe and attach them if they 8947398Sdfr * are alive. 9047398Sdfr */ 9147398Sdfrstatic int 9247398Sdfrisa_probe(device_t dev) 9347398Sdfr{ 9447398Sdfr device_set_desc(dev, "ISA bus"); 9547398Sdfr isa_init(); /* Allow machdep code to initialise */ 9653094Sdfr return 0; 9747398Sdfr} 9847398Sdfr 9947398Sdfrextern device_t isa_bus_device; 10047398Sdfr 10147398Sdfrstatic int 10247398Sdfrisa_attach(device_t dev) 10347398Sdfr{ 10447398Sdfr /* 10550769Sdfr * Arrange for isa_probe_children(dev) to be called later. XXX 10647398Sdfr */ 10747398Sdfr isa_bus_device = dev; 10847398Sdfr return 0; 10947398Sdfr} 11047398Sdfr 11147398Sdfr/* 11250769Sdfr * Find a working set of memory regions for a child using the ranges 11350769Sdfr * in *config and return the regions in *result. Returns non-zero if 11450769Sdfr * a set of ranges was found. 11550769Sdfr */ 11650769Sdfrstatic int 11750769Sdfrisa_find_memory(device_t child, 11850769Sdfr struct isa_config *config, 11950769Sdfr struct isa_config *result) 12050769Sdfr{ 12150769Sdfr int success, i; 12250769Sdfr struct resource *res[ISA_NMEM]; 12350769Sdfr 12450769Sdfr /* 12550769Sdfr * First clear out any existing resource definitions. 12650769Sdfr */ 12750769Sdfr for (i = 0; i < ISA_NMEM; i++) { 12852174Sdfr bus_delete_resource(child, SYS_RES_MEMORY, i); 12950769Sdfr res[i] = NULL; 13050769Sdfr } 13150769Sdfr 13250769Sdfr success = 1; 13350769Sdfr result->ic_nmem = config->ic_nmem; 13450769Sdfr for (i = 0; i < config->ic_nmem; i++) { 13550769Sdfr u_int32_t start, end, size, align; 13650769Sdfr for (start = config->ic_mem[i].ir_start, 13750769Sdfr end = config->ic_mem[i].ir_end, 13850769Sdfr size = config->ic_mem[i].ir_size, 13950769Sdfr align = config->ic_mem[i].ir_align; 14050769Sdfr start + size - 1 <= end; 14150769Sdfr start += align) { 14252174Sdfr bus_set_resource(child, SYS_RES_MEMORY, i, 14350769Sdfr start, size); 14450769Sdfr res[i] = bus_alloc_resource(child, 14550769Sdfr SYS_RES_MEMORY, &i, 14657132Smsmith 0, ~0, 1, 0 /* !RF_ACTIVE */); 14750769Sdfr if (res[i]) { 14850769Sdfr result->ic_mem[i].ir_start = start; 14950769Sdfr result->ic_mem[i].ir_end = start + size - 1; 15050769Sdfr result->ic_mem[i].ir_size = size; 15150769Sdfr result->ic_mem[i].ir_align = align; 15250769Sdfr break; 15350769Sdfr } 15450769Sdfr } 15550769Sdfr 15650769Sdfr /* 15750769Sdfr * If we didn't find a place for memory range i, then 15850769Sdfr * give up now. 15950769Sdfr */ 16050769Sdfr if (!res[i]) { 16150769Sdfr success = 0; 16250769Sdfr break; 16350769Sdfr } 16450769Sdfr } 16550769Sdfr 16650769Sdfr for (i = 0; i < ISA_NMEM; i++) { 16750769Sdfr if (res[i]) 16850769Sdfr bus_release_resource(child, SYS_RES_MEMORY, 16950769Sdfr i, res[i]); 17050769Sdfr } 17150769Sdfr 17250769Sdfr return success; 17350769Sdfr} 17450769Sdfr 17550769Sdfr/* 17650769Sdfr * Find a working set of port regions for a child using the ranges 17750769Sdfr * in *config and return the regions in *result. Returns non-zero if 17850769Sdfr * a set of ranges was found. 17950769Sdfr */ 18050769Sdfrstatic int 18150769Sdfrisa_find_port(device_t child, 18250769Sdfr struct isa_config *config, 18350769Sdfr struct isa_config *result) 18450769Sdfr{ 18550769Sdfr int success, i; 18650769Sdfr struct resource *res[ISA_NPORT]; 18750769Sdfr 18850769Sdfr /* 18950769Sdfr * First clear out any existing resource definitions. 19050769Sdfr */ 19150769Sdfr for (i = 0; i < ISA_NPORT; i++) { 19252174Sdfr bus_delete_resource(child, SYS_RES_IOPORT, i); 19350769Sdfr res[i] = NULL; 19450769Sdfr } 19550769Sdfr 19650769Sdfr success = 1; 19750769Sdfr result->ic_nport = config->ic_nport; 19850769Sdfr for (i = 0; i < config->ic_nport; i++) { 19950769Sdfr u_int32_t start, end, size, align; 20050769Sdfr for (start = config->ic_port[i].ir_start, 20150769Sdfr end = config->ic_port[i].ir_end, 20250769Sdfr size = config->ic_port[i].ir_size, 20350769Sdfr align = config->ic_port[i].ir_align; 20450769Sdfr start + size - 1 <= end; 20550769Sdfr start += align) { 20652174Sdfr bus_set_resource(child, SYS_RES_IOPORT, i, 20750769Sdfr start, size); 20850769Sdfr res[i] = bus_alloc_resource(child, 20950769Sdfr SYS_RES_IOPORT, &i, 21057132Smsmith 0, ~0, 1, 0 /* !RF_ACTIVE */); 21150769Sdfr if (res[i]) { 21250769Sdfr result->ic_port[i].ir_start = start; 21350769Sdfr result->ic_port[i].ir_end = start + size - 1; 21450769Sdfr result->ic_port[i].ir_size = size; 21550769Sdfr result->ic_port[i].ir_align = align; 21650769Sdfr break; 21750769Sdfr } 21850769Sdfr } 21950769Sdfr 22050769Sdfr /* 22150769Sdfr * If we didn't find a place for port range i, then 22250769Sdfr * give up now. 22350769Sdfr */ 22450769Sdfr if (!res[i]) { 22550769Sdfr success = 0; 22650769Sdfr break; 22750769Sdfr } 22850769Sdfr } 22950769Sdfr 23050769Sdfr for (i = 0; i < ISA_NPORT; i++) { 23150769Sdfr if (res[i]) 23250769Sdfr bus_release_resource(child, SYS_RES_IOPORT, 23350769Sdfr i, res[i]); 23450769Sdfr } 23550769Sdfr 23650769Sdfr return success; 23750769Sdfr} 23850769Sdfr 23950769Sdfr/* 24050769Sdfr * Return the index of the first bit in the mask (or -1 if mask is empty. 24150769Sdfr */ 24250769Sdfrstatic int 24350769Sdfrfind_first_bit(u_int32_t mask) 24450769Sdfr{ 24550769Sdfr return ffs(mask) - 1; 24650769Sdfr} 24750769Sdfr 24850769Sdfr/* 24950769Sdfr * Return the index of the next bit in the mask, or -1 if there are no more. 25050769Sdfr */ 25150769Sdfrstatic int 25250769Sdfrfind_next_bit(u_int32_t mask, int bit) 25350769Sdfr{ 25450769Sdfr bit++; 25550769Sdfr while (bit < 32 && !(mask & (1 << bit))) 25650769Sdfr bit++; 25750769Sdfr if (bit != 32) 25850769Sdfr return bit; 25950769Sdfr return -1; 26050769Sdfr} 26150769Sdfr 26250769Sdfr/* 26350769Sdfr * Find a working set of irqs for a child using the masks in *config 26450769Sdfr * and return the regions in *result. Returns non-zero if a set of 26550769Sdfr * irqs was found. 26650769Sdfr */ 26750769Sdfrstatic int 26850769Sdfrisa_find_irq(device_t child, 26950769Sdfr struct isa_config *config, 27050769Sdfr struct isa_config *result) 27150769Sdfr{ 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++) { 27952174Sdfr bus_delete_resource(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)) { 29152174Sdfr bus_set_resource(child, SYS_RES_IRQ, i, 29250769Sdfr irq, 1); 29350769Sdfr res[i] = bus_alloc_resource(child, 29450769Sdfr SYS_RES_IRQ, &i, 29557132Smsmith 0, ~0, 1, 0 /* !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 int success, i; 33250769Sdfr struct resource *res[ISA_NDRQ]; 33350769Sdfr 33450769Sdfr /* 33550769Sdfr * First clear out any existing resource definitions. 33650769Sdfr */ 33750769Sdfr for (i = 0; i < ISA_NDRQ; i++) { 33852174Sdfr bus_delete_resource(child, SYS_RES_DRQ, i); 33950769Sdfr res[i] = NULL; 34050769Sdfr } 34150769Sdfr 34250769Sdfr success = 1; 34350769Sdfr result->ic_ndrq = config->ic_ndrq; 34450769Sdfr for (i = 0; i < config->ic_ndrq; i++) { 34550769Sdfr u_int32_t mask = config->ic_drqmask[i]; 34650769Sdfr int drq; 34750769Sdfr for (drq = find_first_bit(mask); 34850769Sdfr drq != -1; 34950769Sdfr drq = find_next_bit(mask, drq)) { 35052174Sdfr bus_set_resource(child, SYS_RES_DRQ, i, 35150769Sdfr drq, 1); 35250769Sdfr res[i] = bus_alloc_resource(child, 35350769Sdfr SYS_RES_DRQ, &i, 35457132Smsmith 0, ~0, 1, 0 /* !RF_ACTIVE */); 35550769Sdfr if (res[i]) { 35650769Sdfr result->ic_drqmask[i] = (1 << drq); 35750769Sdfr break; 35850769Sdfr } 35950769Sdfr } 36050769Sdfr 36150769Sdfr /* 36250769Sdfr * If we didn't find a place for drq range i, then 36350769Sdfr * give up now. 36450769Sdfr */ 36550769Sdfr if (!res[i]) { 36650769Sdfr success = 0; 36750769Sdfr break; 36850769Sdfr } 36950769Sdfr } 37050769Sdfr 37150769Sdfr for (i = 0; i < ISA_NDRQ; i++) { 37250769Sdfr if (res[i]) 37350769Sdfr bus_release_resource(child, SYS_RES_DRQ, 37450769Sdfr i, res[i]); 37550769Sdfr } 37650769Sdfr 37750769Sdfr return success; 37850769Sdfr} 37950769Sdfr 38050769Sdfr/* 38150769Sdfr * Attempt to find a working set of resources for a device. Return 38250769Sdfr * non-zero if a working configuration is found. 38350769Sdfr */ 38450769Sdfrstatic int 38550769Sdfrisa_assign_resources(device_t child) 38650769Sdfr{ 38750769Sdfr struct isa_device *idev = DEVTOISA(child); 38850769Sdfr struct isa_config_entry *ice; 38950769Sdfr struct isa_config config; 39050769Sdfr 39150769Sdfr bzero(&config, sizeof config); 39250769Sdfr TAILQ_FOREACH(ice, &idev->id_configs, ice_link) { 39350769Sdfr if (!isa_find_memory(child, &ice->ice_config, &config)) 39450769Sdfr continue; 39550769Sdfr if (!isa_find_port(child, &ice->ice_config, &config)) 39650769Sdfr continue; 39750769Sdfr if (!isa_find_irq(child, &ice->ice_config, &config)) 39850769Sdfr continue; 39950769Sdfr if (!isa_find_drq(child, &ice->ice_config, &config)) 40050769Sdfr continue; 40150769Sdfr 40250769Sdfr /* 40350769Sdfr * A working configuration was found enable the device 40450769Sdfr * with this configuration. 40550769Sdfr */ 40650769Sdfr if (idev->id_config_cb) { 40750769Sdfr idev->id_config_cb(idev->id_config_arg, 40850769Sdfr &config, 1); 40950769Sdfr return 1; 41050769Sdfr } 41150769Sdfr } 41250769Sdfr 41350769Sdfr /* 41450769Sdfr * Disable the device. 41550769Sdfr */ 41662987Sjhb bus_print_child_header(device_get_parent(child), child); 41762987Sjhb printf(" can't assign resources\n"); 41862987Sjhb if (bootverbose) 41962987Sjhb isa_print_child(device_get_parent(child), child); 42050769Sdfr bzero(&config, sizeof config); 42150769Sdfr if (idev->id_config_cb) 42250769Sdfr idev->id_config_cb(idev->id_config_arg, &config, 0); 42350769Sdfr device_disable(child); 42450769Sdfr 42550769Sdfr return 0; 42650769Sdfr} 42750769Sdfr 42850769Sdfr/* 42950769Sdfr * Called after other devices have initialised to probe for isa devices. 43050769Sdfr */ 43150769Sdfrvoid 43250769Sdfrisa_probe_children(device_t dev) 43350769Sdfr{ 43450769Sdfr device_t *children; 43550769Sdfr int nchildren, i; 43650769Sdfr 43753094Sdfr /* 43853094Sdfr * Create all the children by calling driver's identify methods. 43953094Sdfr */ 44053094Sdfr bus_generic_probe(dev); 44153094Sdfr 44250769Sdfr if (device_get_children(dev, &children, &nchildren)) 44350769Sdfr return; 44450769Sdfr 44550769Sdfr /* 44651905Sdfr * First disable all pnp devices so that they don't get 44751905Sdfr * matched by legacy probes. 44851905Sdfr */ 44953094Sdfr if (bootverbose) 45053094Sdfr printf("isa_probe_children: disabling PnP devices\n"); 45151905Sdfr for (i = 0; i < nchildren; i++) { 45251905Sdfr device_t child = children[i]; 45351905Sdfr struct isa_device *idev = DEVTOISA(child); 45451905Sdfr struct isa_config config; 45551905Sdfr 45651905Sdfr bzero(&config, sizeof config); 45751905Sdfr if (idev->id_config_cb) 45851905Sdfr idev->id_config_cb(idev->id_config_arg, &config, 0); 45951905Sdfr } 46051905Sdfr 46151905Sdfr /* 46251905Sdfr * Next probe all non-pnp devices so that they claim their 46350769Sdfr * resources first. 46450769Sdfr */ 46553094Sdfr if (bootverbose) 46653094Sdfr printf("isa_probe_children: probing non-PnP devices\n"); 46750769Sdfr for (i = 0; i < nchildren; i++) { 46850769Sdfr device_t child = children[i]; 46950769Sdfr struct isa_device *idev = DEVTOISA(child); 47050769Sdfr 47150769Sdfr if (TAILQ_FIRST(&idev->id_configs)) 47250769Sdfr continue; 47350769Sdfr 47450769Sdfr device_probe_and_attach(child); 47550769Sdfr } 47650769Sdfr 47750769Sdfr /* 47851905Sdfr * Finally assign resource to pnp devices and probe them. 47950769Sdfr */ 48053094Sdfr if (bootverbose) 48153094Sdfr printf("isa_probe_children: probing PnP devices\n"); 48250769Sdfr for (i = 0; i < nchildren; i++) { 48350769Sdfr device_t child = children[i]; 48450769Sdfr struct isa_device* idev = DEVTOISA(child); 48550769Sdfr 48650769Sdfr if (!TAILQ_FIRST(&idev->id_configs)) 48750769Sdfr continue; 48850769Sdfr 48950769Sdfr if (isa_assign_resources(child)) { 49052174Sdfr struct resource_list *rl = &idev->id_resources; 49150769Sdfr struct resource_list_entry *rle; 49250769Sdfr 49350769Sdfr device_probe_and_attach(child); 49450769Sdfr 49550769Sdfr /* 49650769Sdfr * Claim any unallocated resources to keep other 49750769Sdfr * devices from using them. 49850769Sdfr */ 49952174Sdfr SLIST_FOREACH(rle, rl, link) { 50050769Sdfr if (!rle->res) { 50150769Sdfr int rid = rle->rid; 50252174Sdfr resource_list_alloc(rl, dev, child, 50350769Sdfr rle->type, 50450769Sdfr &rid, 50562059Sdfr 0, ~0, 1, 0); 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 53962059Sdfrstatic int 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; 54562059Sdfr int i, retval = 0;; 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) 55262059Sdfr retval += printf(" %s ", name); 55350769Sdfr else if (printed > 0) 55462059Sdfr retval += printf(","); 55550769Sdfr printed++; 55662059Sdfr retval += printf(format, rle->start); 55750769Sdfr if (rle->count > 1) { 55862059Sdfr retval += printf("-"); 55962059Sdfr retval += printf(format, 56062059Sdfr rle->start + rle->count - 1); 56147398Sdfr } 56250769Sdfr } else if (i > 3) { 56350769Sdfr /* check the first few regardless */ 56450769Sdfr break; 56547398Sdfr } 56647398Sdfr } 56762059Sdfr return retval; 56847398Sdfr} 56947398Sdfr 57049195Smdoddstatic int 57162059Sdfrisa_print_all_resources(device_t dev) 57247398Sdfr{ 57347398Sdfr struct isa_device *idev = DEVTOISA(dev); 57447398Sdfr struct resource_list *rl = &idev->id_resources; 57549195Smdodd int retval = 0; 57647398Sdfr 57751052Sdfr if (SLIST_FIRST(rl) || device_get_flags(dev)) 57849195Smdodd retval += printf(" at"); 57947398Sdfr 58062059Sdfr retval += isa_print_resources(rl, "port", SYS_RES_IOPORT, 58162059Sdfr ISA_NPORT, "%#lx"); 58262059Sdfr retval += isa_print_resources(rl, "iomem", SYS_RES_MEMORY, 58362059Sdfr ISA_NMEM, "%#lx"); 58462059Sdfr retval += isa_print_resources(rl, "irq", SYS_RES_IRQ, 58562059Sdfr ISA_NIRQ, "%ld"); 58662059Sdfr retval += isa_print_resources(rl, "drq", SYS_RES_DRQ, 58762059Sdfr ISA_NDRQ, "%ld"); 58851052Sdfr if (device_get_flags(dev)) 58951052Sdfr retval += printf(" flags %#x", device_get_flags(dev)); 59047398Sdfr 59162059Sdfr return retval; 59262059Sdfr} 59362059Sdfr 59462059Sdfrstatic int 59562059Sdfrisa_print_child(device_t bus, device_t dev) 59662059Sdfr{ 59762059Sdfr int retval = 0; 59862059Sdfr 59962059Sdfr retval += bus_print_child_header(bus, dev); 60062059Sdfr retval += isa_print_all_resources(dev); 60149195Smdodd retval += bus_print_child_footer(bus, dev); 60249195Smdodd 60349195Smdodd return (retval); 60447398Sdfr} 60547398Sdfr 60662059Sdfrstatic void 60762059Sdfrisa_probe_nomatch(device_t dev, device_t child) 60862059Sdfr{ 60962987Sjhb if (bootverbose) { 61062987Sjhb bus_print_child_header(dev, child); 61162987Sjhb printf(" failed to probe"); 61262059Sdfr isa_print_all_resources(child); 61362987Sjhb bus_print_child_footer(dev, child); 61462987Sjhb } 61562059Sdfr 61662059Sdfr return; 61762059Sdfr} 61862059Sdfr 61947398Sdfrstatic int 62047398Sdfrisa_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result) 62147398Sdfr{ 62247398Sdfr struct isa_device* idev = DEVTOISA(dev); 62347398Sdfr struct resource_list *rl = &idev->id_resources; 62447398Sdfr struct resource_list_entry *rle; 62547398Sdfr 62647398Sdfr switch (index) { 62747398Sdfr case ISA_IVAR_PORT_0: 62847398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 0); 62947398Sdfr if (rle) 63047398Sdfr *result = rle->start; 63147398Sdfr else 63247398Sdfr *result = -1; 63347398Sdfr break; 63447398Sdfr 63547398Sdfr case ISA_IVAR_PORT_1: 63647398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 1); 63747398Sdfr if (rle) 63847398Sdfr *result = rle->start; 63947398Sdfr else 64047398Sdfr *result = -1; 64147398Sdfr break; 64247398Sdfr 64347398Sdfr case ISA_IVAR_PORTSIZE_0: 64447398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 0); 64547398Sdfr if (rle) 64647398Sdfr *result = rle->count; 64747398Sdfr else 64847398Sdfr *result = 0; 64947398Sdfr break; 65047398Sdfr 65147398Sdfr case ISA_IVAR_PORTSIZE_1: 65247398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 1); 65347398Sdfr if (rle) 65447398Sdfr *result = rle->count; 65547398Sdfr else 65647398Sdfr *result = 0; 65747398Sdfr break; 65847398Sdfr 65947398Sdfr case ISA_IVAR_MADDR_0: 66047398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 0); 66147398Sdfr if (rle) 66247398Sdfr *result = rle->start; 66347398Sdfr else 66447398Sdfr *result = -1; 66547398Sdfr break; 66647398Sdfr 66747398Sdfr case ISA_IVAR_MADDR_1: 66847398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 1); 66947398Sdfr if (rle) 67047398Sdfr *result = rle->start; 67147398Sdfr else 67247398Sdfr *result = -1; 67347398Sdfr break; 67447398Sdfr 67547398Sdfr case ISA_IVAR_MSIZE_0: 67647398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 0); 67747398Sdfr if (rle) 67847398Sdfr *result = rle->count; 67947398Sdfr else 68047398Sdfr *result = 0; 68147398Sdfr break; 68247398Sdfr 68347398Sdfr case ISA_IVAR_MSIZE_1: 68447398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 1); 68547398Sdfr if (rle) 68647398Sdfr *result = rle->count; 68747398Sdfr else 68847398Sdfr *result = 0; 68947398Sdfr break; 69047398Sdfr 69147398Sdfr case ISA_IVAR_IRQ_0: 69247398Sdfr rle = resource_list_find(rl, SYS_RES_IRQ, 0); 69347398Sdfr if (rle) 69447398Sdfr *result = rle->start; 69547398Sdfr else 69647398Sdfr *result = -1; 69747398Sdfr break; 69847398Sdfr 69947398Sdfr case ISA_IVAR_IRQ_1: 70047398Sdfr rle = resource_list_find(rl, SYS_RES_IRQ, 1); 70147398Sdfr if (rle) 70247398Sdfr *result = rle->start; 70347398Sdfr else 70447398Sdfr *result = -1; 70547398Sdfr break; 70647398Sdfr 70747398Sdfr case ISA_IVAR_DRQ_0: 70847398Sdfr rle = resource_list_find(rl, SYS_RES_DRQ, 0); 70947398Sdfr if (rle) 71047398Sdfr *result = rle->start; 71147398Sdfr else 71247398Sdfr *result = -1; 71347398Sdfr break; 71447398Sdfr 71547398Sdfr case ISA_IVAR_DRQ_1: 71647398Sdfr rle = resource_list_find(rl, SYS_RES_DRQ, 1); 71747398Sdfr if (rle) 71847398Sdfr *result = rle->start; 71947398Sdfr else 72047398Sdfr *result = -1; 72147398Sdfr break; 72247398Sdfr 72347613Sdfr case ISA_IVAR_VENDORID: 72447613Sdfr *result = idev->id_vendorid; 72547613Sdfr break; 72647613Sdfr 72747613Sdfr case ISA_IVAR_SERIAL: 72847613Sdfr *result = idev->id_serial; 72947613Sdfr break; 73047613Sdfr 73147613Sdfr case ISA_IVAR_LOGICALID: 73247613Sdfr *result = idev->id_logicalid; 73347613Sdfr break; 73447613Sdfr 73547613Sdfr case ISA_IVAR_COMPATID: 73647613Sdfr *result = idev->id_compatid; 73747613Sdfr break; 73847613Sdfr 73947613Sdfr default: 74047613Sdfr return ENOENT; 74147398Sdfr } 74247613Sdfr 74347613Sdfr return 0; 74447398Sdfr} 74547398Sdfr 74647398Sdfrstatic int 74747398Sdfrisa_write_ivar(device_t bus, device_t dev, 74847398Sdfr int index, uintptr_t value) 74947398Sdfr{ 75047398Sdfr struct isa_device* idev = DEVTOISA(dev); 75147398Sdfr 75247398Sdfr switch (index) { 75347398Sdfr case ISA_IVAR_PORT_0: 75447398Sdfr case ISA_IVAR_PORT_1: 75547398Sdfr case ISA_IVAR_PORTSIZE_0: 75647398Sdfr case ISA_IVAR_PORTSIZE_1: 75747398Sdfr case ISA_IVAR_MADDR_0: 75847398Sdfr case ISA_IVAR_MADDR_1: 75947398Sdfr case ISA_IVAR_MSIZE_0: 76047398Sdfr case ISA_IVAR_MSIZE_1: 76147398Sdfr case ISA_IVAR_IRQ_0: 76247398Sdfr case ISA_IVAR_IRQ_1: 76347398Sdfr case ISA_IVAR_DRQ_0: 76447398Sdfr case ISA_IVAR_DRQ_1: 76547398Sdfr return EINVAL; 76647398Sdfr 76747613Sdfr case ISA_IVAR_VENDORID: 76847613Sdfr idev->id_vendorid = value; 76947613Sdfr break; 77047613Sdfr 77147613Sdfr case ISA_IVAR_SERIAL: 77247613Sdfr idev->id_serial = value; 77347613Sdfr break; 77447613Sdfr 77547613Sdfr case ISA_IVAR_LOGICALID: 77647613Sdfr idev->id_logicalid = value; 77747613Sdfr break; 77847613Sdfr 77947613Sdfr case ISA_IVAR_COMPATID: 78047613Sdfr idev->id_compatid = value; 78147613Sdfr break; 78247613Sdfr 78347398Sdfr default: 78447398Sdfr return (ENOENT); 78547398Sdfr } 78647613Sdfr 78747398Sdfr return (0); 78847398Sdfr} 78947398Sdfr 79050769Sdfr/* 79150769Sdfr * Free any resources which the driver missed or which we were holding for 79250769Sdfr * it (see isa_probe_children). 79350769Sdfr */ 79450769Sdfrstatic void 79550769Sdfrisa_child_detached(device_t dev, device_t child) 79650769Sdfr{ 79750769Sdfr struct isa_device* idev = DEVTOISA(child); 79852174Sdfr struct resource_list *rl = &idev->id_resources; 79950769Sdfr struct resource_list_entry *rle; 80050769Sdfr 80162059Sdfr if (TAILQ_FIRST(&idev->id_configs)) { 80262059Sdfr /* 80362059Sdfr * Claim any unallocated resources to keep other 80462059Sdfr * devices from using them. 80562059Sdfr */ 80662059Sdfr SLIST_FOREACH(rle, rl, link) { 80762059Sdfr if (!rle->res) { 80862059Sdfr int rid = rle->rid; 80962059Sdfr resource_list_alloc(rl, dev, child, 81062059Sdfr rle->type, 81162059Sdfr &rid, 0, ~0, 1, 0); 81262059Sdfr } 81362059Sdfr } 81450769Sdfr } 81550769Sdfr} 81650769Sdfr 81753094Sdfrstatic void 81853094Sdfrisa_driver_added(device_t dev, driver_t *driver) 81953094Sdfr{ 82053094Sdfr device_t *children; 82153094Sdfr int nchildren, i; 82253094Sdfr 82353094Sdfr /* 82453094Sdfr * Don't do anything if drivers are dynamically 82553094Sdfr * added during autoconfiguration (cf. ymf724). 82653094Sdfr * since that would end up calling identify 82753094Sdfr * twice. 82853094Sdfr */ 82953094Sdfr if (!isa_running) 83053094Sdfr return; 83153094Sdfr 83253094Sdfr DEVICE_IDENTIFY(driver, dev); 83353094Sdfr if (device_get_children(dev, &children, &nchildren)) 83453094Sdfr return; 83553094Sdfr 83653094Sdfr for (i = 0; i < nchildren; i++) { 83753094Sdfr device_t child = children[i]; 83853094Sdfr struct isa_device *idev = DEVTOISA(child); 83953094Sdfr struct resource_list *rl = &idev->id_resources; 84053094Sdfr struct resource_list_entry *rle; 84153094Sdfr 84253094Sdfr if (device_get_state(child) != DS_NOTPRESENT) 84353094Sdfr continue; 84462059Sdfr if (!device_is_enabled(child)) 84562059Sdfr continue; 84653094Sdfr 84762059Sdfr /* 84862059Sdfr * Free resources which we were holding on behalf of 84962059Sdfr * the device. 85062059Sdfr */ 85162059Sdfr SLIST_FOREACH(rle, &idev->id_resources, link) { 85262059Sdfr if (rle->res) 85362059Sdfr resource_list_release(rl, dev, child, 85462059Sdfr rle->type, 85562059Sdfr rle->rid, 85662059Sdfr rle->res); 85762059Sdfr } 85862059Sdfr 85953094Sdfr if (TAILQ_FIRST(&idev->id_configs)) 86053094Sdfr if (!isa_assign_resources(child)) 86153094Sdfr continue; 86253094Sdfr 86353094Sdfr device_probe_and_attach(child); 86453094Sdfr 86553094Sdfr if (TAILQ_FIRST(&idev->id_configs)) { 86653094Sdfr /* 86753094Sdfr * Claim any unallocated resources to keep other 86853094Sdfr * devices from using them. 86953094Sdfr */ 87053094Sdfr SLIST_FOREACH(rle, rl, link) { 87153094Sdfr if (!rle->res) { 87253094Sdfr int rid = rle->rid; 87353094Sdfr resource_list_alloc(rl, dev, child, 87453094Sdfr rle->type, 87562059Sdfr &rid, 0, ~0, 1, 0); 87653094Sdfr } 87753094Sdfr } 87853094Sdfr } 87953094Sdfr } 88053094Sdfr 88153094Sdfr free(children, M_TEMP); 88253094Sdfr} 88353094Sdfr 88447398Sdfrstatic int 88547398Sdfrisa_set_resource(device_t dev, device_t child, int type, int rid, 88647398Sdfr u_long start, u_long count) 88747398Sdfr{ 88847398Sdfr struct isa_device* idev = DEVTOISA(child); 88947398Sdfr struct resource_list *rl = &idev->id_resources; 89047398Sdfr 89147398Sdfr if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY 89247398Sdfr && type != SYS_RES_IRQ && type != SYS_RES_DRQ) 89347398Sdfr return EINVAL; 89450769Sdfr if (rid < 0) 89547398Sdfr return EINVAL; 89650769Sdfr if (type == SYS_RES_IOPORT && rid >= ISA_NPORT) 89750769Sdfr return EINVAL; 89850769Sdfr if (type == SYS_RES_MEMORY && rid >= ISA_NMEM) 89950769Sdfr return EINVAL; 90050769Sdfr if (type == SYS_RES_IRQ && rid >= ISA_NIRQ) 90150769Sdfr return EINVAL; 90250769Sdfr if (type == SYS_RES_DRQ && rid >= ISA_NDRQ) 90350769Sdfr return EINVAL; 90447398Sdfr 90547398Sdfr resource_list_add(rl, type, rid, start, start + count - 1, count); 90647398Sdfr 90747398Sdfr return 0; 90847398Sdfr} 90947398Sdfr 91069295Smdoddstatic struct resource_list * 91169295Smdoddisa_get_resource_list (device_t dev, device_t child) 91247398Sdfr{ 91347398Sdfr struct isa_device* idev = DEVTOISA(child); 91447398Sdfr struct resource_list *rl = &idev->id_resources; 91547398Sdfr 91669295Smdodd if (!rl) 91769295Smdodd return (NULL); 91847398Sdfr 91969295Smdodd return (rl); 92047398Sdfr} 92147398Sdfr 92250769Sdfrstatic int 92350769Sdfrisa_add_config(device_t dev, device_t child, 92450769Sdfr int priority, struct isa_config *config) 92550769Sdfr{ 92650769Sdfr struct isa_device* idev = DEVTOISA(child); 92750769Sdfr struct isa_config_entry *newice, *ice; 92850769Sdfr 92950769Sdfr newice = malloc(sizeof *ice, M_DEVBUF, M_NOWAIT); 93050769Sdfr if (!newice) 93150769Sdfr return ENOMEM; 93250769Sdfr 93350769Sdfr newice->ice_priority = priority; 93450769Sdfr newice->ice_config = *config; 93550769Sdfr 93650769Sdfr TAILQ_FOREACH(ice, &idev->id_configs, ice_link) { 93750769Sdfr if (ice->ice_priority > priority) 93850769Sdfr break; 93950769Sdfr } 94050769Sdfr if (ice) 94150769Sdfr TAILQ_INSERT_BEFORE(ice, newice, ice_link); 94250769Sdfr else 94350769Sdfr TAILQ_INSERT_TAIL(&idev->id_configs, newice, ice_link); 94450769Sdfr 94550769Sdfr return 0; 94650769Sdfr} 94750769Sdfr 94850769Sdfrstatic void 94950769Sdfrisa_set_config_callback(device_t dev, device_t child, 95050769Sdfr isa_config_cb *fn, void *arg) 95150769Sdfr{ 95250769Sdfr struct isa_device* idev = DEVTOISA(child); 95350769Sdfr 95450769Sdfr idev->id_config_cb = fn; 95550769Sdfr idev->id_config_arg = arg; 95650769Sdfr} 95750769Sdfr 95850769Sdfrstatic int 95950769Sdfrisa_pnp_probe(device_t dev, device_t child, struct isa_pnp_id *ids) 96050769Sdfr{ 96150769Sdfr struct isa_device* idev = DEVTOISA(child); 96250769Sdfr 96350769Sdfr if (!idev->id_vendorid) 96450769Sdfr return ENOENT; 96550769Sdfr 96650769Sdfr while (ids->ip_id) { 96750769Sdfr /* 96850769Sdfr * Really ought to support >1 compat id per device. 96950769Sdfr */ 97050769Sdfr if (idev->id_logicalid == ids->ip_id 97150769Sdfr || idev->id_compatid == ids->ip_id) { 97250910Sdfr if (ids->ip_desc) 97350910Sdfr device_set_desc(child, ids->ip_desc); 97450769Sdfr return 0; 97550769Sdfr } 97650769Sdfr ids++; 97750769Sdfr } 97850769Sdfr 97950769Sdfr return ENXIO; 98050769Sdfr} 98150769Sdfr 98247398Sdfrstatic device_method_t isa_methods[] = { 98347398Sdfr /* Device interface */ 98447398Sdfr DEVMETHOD(device_probe, isa_probe), 98547398Sdfr DEVMETHOD(device_attach, isa_attach), 98647398Sdfr DEVMETHOD(device_detach, bus_generic_detach), 98747398Sdfr DEVMETHOD(device_shutdown, bus_generic_shutdown), 98847398Sdfr DEVMETHOD(device_suspend, bus_generic_suspend), 98947398Sdfr DEVMETHOD(device_resume, bus_generic_resume), 99047398Sdfr 99147398Sdfr /* Bus interface */ 99247398Sdfr DEVMETHOD(bus_add_child, isa_add_child), 99347398Sdfr DEVMETHOD(bus_print_child, isa_print_child), 99462059Sdfr DEVMETHOD(bus_probe_nomatch, isa_probe_nomatch), 99547398Sdfr DEVMETHOD(bus_read_ivar, isa_read_ivar), 99647398Sdfr DEVMETHOD(bus_write_ivar, isa_write_ivar), 99750769Sdfr DEVMETHOD(bus_child_detached, isa_child_detached), 99853094Sdfr DEVMETHOD(bus_driver_added, isa_driver_added), 99969295Smdodd DEVMETHOD(bus_setup_intr, isa_setup_intr), 100069295Smdodd DEVMETHOD(bus_teardown_intr, isa_teardown_intr), 100169295Smdodd 100269295Smdodd DEVMETHOD(bus_get_resource_list,isa_get_resource_list), 100347398Sdfr DEVMETHOD(bus_alloc_resource, isa_alloc_resource), 100447398Sdfr DEVMETHOD(bus_release_resource, isa_release_resource), 100569295Smdodd DEVMETHOD(bus_set_resource, isa_set_resource), 100669295Smdodd DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 100769295Smdodd DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), 100847398Sdfr DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 100947398Sdfr DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 101047398Sdfr 101147398Sdfr /* ISA interface */ 101250769Sdfr DEVMETHOD(isa_add_config, isa_add_config), 101350769Sdfr DEVMETHOD(isa_set_config_callback, isa_set_config_callback), 101450769Sdfr DEVMETHOD(isa_pnp_probe, isa_pnp_probe), 101547398Sdfr 101647398Sdfr { 0, 0 } 101747398Sdfr}; 101847398Sdfr 101947398Sdfrstatic driver_t isa_driver = { 102047398Sdfr "isa", 102147398Sdfr isa_methods, 102247398Sdfr 1, /* no softc */ 102347398Sdfr}; 102447398Sdfr 102547398Sdfr/* 102647398Sdfr * ISA can be attached to a PCI-ISA bridge or directly to the nexus. 102747398Sdfr */ 102847398SdfrDRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0); 102947398Sdfr#ifdef __i386__ 103047398SdfrDRIVER_MODULE(isa, nexus, isa_driver, isa_devclass, 0, 0); 103147398Sdfr#endif 1032