isa_common.c revision 62059
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 62059 2000-06-25 09:19:02Z 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; 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, 14457132Smsmith 0, ~0, 1, 0 /* !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, 20857132Smsmith 0, ~0, 1, 0 /* !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, 29357132Smsmith 0, ~0, 1, 0 /* !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, 35257132Smsmith 0, ~0, 1, 0 /* !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, 50462059Sdfr 0, ~0, 1, 0); 50550769Sdfr } 50650769Sdfr } 50750769Sdfr } 50850769Sdfr } 50950769Sdfr 51050769Sdfr free(children, M_TEMP); 51153094Sdfr 51253094Sdfr isa_running = 1; 51350769Sdfr} 51450769Sdfr 51550769Sdfr/* 51647398Sdfr * Add a new child with default ivars. 51747398Sdfr */ 51847398Sdfrstatic device_t 51947578Sdfrisa_add_child(device_t dev, int order, const char *name, int unit) 52047398Sdfr{ 52154073Smdodd device_t child; 52247398Sdfr struct isa_device *idev; 52347398Sdfr 52447398Sdfr idev = malloc(sizeof(struct isa_device), M_ISADEV, M_NOWAIT); 52547398Sdfr if (!idev) 52647398Sdfr return 0; 52747398Sdfr bzero(idev, sizeof *idev); 52847398Sdfr 52947398Sdfr resource_list_init(&idev->id_resources); 53050769Sdfr TAILQ_INIT(&idev->id_configs); 53147398Sdfr 53254073Smdodd child = device_add_child_ordered(dev, order, name, unit); 53354073Smdodd device_set_ivars(child, idev); 53454073Smdodd 53554073Smdodd return child; 53647398Sdfr} 53747398Sdfr 53862059Sdfrstatic int 53947398Sdfrisa_print_resources(struct resource_list *rl, const char *name, int type, 54052174Sdfr int count, const char *format) 54147398Sdfr{ 54250769Sdfr struct resource_list_entry *rle; 54350769Sdfr int printed; 54462059Sdfr int i, retval = 0;; 54547398Sdfr 54650769Sdfr printed = 0; 54752174Sdfr for (i = 0; i < count; i++) { 54850769Sdfr rle = resource_list_find(rl, type, i); 54950769Sdfr if (rle) { 55050769Sdfr if (printed == 0) 55162059Sdfr retval += printf(" %s ", name); 55250769Sdfr else if (printed > 0) 55362059Sdfr retval += printf(","); 55450769Sdfr printed++; 55562059Sdfr retval += printf(format, rle->start); 55650769Sdfr if (rle->count > 1) { 55762059Sdfr retval += printf("-"); 55862059Sdfr retval += printf(format, 55962059Sdfr rle->start + rle->count - 1); 56047398Sdfr } 56150769Sdfr } else if (i > 3) { 56250769Sdfr /* check the first few regardless */ 56350769Sdfr break; 56447398Sdfr } 56547398Sdfr } 56662059Sdfr return retval; 56747398Sdfr} 56847398Sdfr 56949195Smdoddstatic int 57062059Sdfrisa_print_all_resources(device_t dev) 57147398Sdfr{ 57247398Sdfr struct isa_device *idev = DEVTOISA(dev); 57347398Sdfr struct resource_list *rl = &idev->id_resources; 57449195Smdodd int retval = 0; 57547398Sdfr 57651052Sdfr if (SLIST_FIRST(rl) || device_get_flags(dev)) 57749195Smdodd retval += printf(" at"); 57847398Sdfr 57962059Sdfr retval += isa_print_resources(rl, "port", SYS_RES_IOPORT, 58062059Sdfr ISA_NPORT, "%#lx"); 58162059Sdfr retval += isa_print_resources(rl, "iomem", SYS_RES_MEMORY, 58262059Sdfr ISA_NMEM, "%#lx"); 58362059Sdfr retval += isa_print_resources(rl, "irq", SYS_RES_IRQ, 58462059Sdfr ISA_NIRQ, "%ld"); 58562059Sdfr retval += isa_print_resources(rl, "drq", SYS_RES_DRQ, 58662059Sdfr ISA_NDRQ, "%ld"); 58751052Sdfr if (device_get_flags(dev)) 58851052Sdfr retval += printf(" flags %#x", device_get_flags(dev)); 58947398Sdfr 59062059Sdfr return retval; 59162059Sdfr} 59262059Sdfr 59362059Sdfrstatic int 59462059Sdfrisa_print_child(device_t bus, device_t dev) 59562059Sdfr{ 59662059Sdfr int retval = 0; 59762059Sdfr 59862059Sdfr retval += bus_print_child_header(bus, dev); 59962059Sdfr retval += isa_print_all_resources(dev); 60049195Smdodd retval += bus_print_child_footer(bus, dev); 60149195Smdodd 60249195Smdodd return (retval); 60347398Sdfr} 60447398Sdfr 60562059Sdfrstatic void 60662059Sdfrisa_probe_nomatch(device_t dev, device_t child) 60762059Sdfr{ 60862059Sdfr device_printf(dev, "<%s> found", 60962059Sdfr pnp_eisaformat(isa_get_logicalid(child))); 61062059Sdfr if (bootverbose) 61162059Sdfr isa_print_all_resources(child); 61262059Sdfr printf("\n"); 61362059Sdfr 61462059Sdfr return; 61562059Sdfr} 61662059Sdfr 61747398Sdfrstatic int 61847398Sdfrisa_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result) 61947398Sdfr{ 62047398Sdfr struct isa_device* idev = DEVTOISA(dev); 62147398Sdfr struct resource_list *rl = &idev->id_resources; 62247398Sdfr struct resource_list_entry *rle; 62347398Sdfr 62447398Sdfr switch (index) { 62547398Sdfr case ISA_IVAR_PORT_0: 62647398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 0); 62747398Sdfr if (rle) 62847398Sdfr *result = rle->start; 62947398Sdfr else 63047398Sdfr *result = -1; 63147398Sdfr break; 63247398Sdfr 63347398Sdfr case ISA_IVAR_PORT_1: 63447398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 1); 63547398Sdfr if (rle) 63647398Sdfr *result = rle->start; 63747398Sdfr else 63847398Sdfr *result = -1; 63947398Sdfr break; 64047398Sdfr 64147398Sdfr case ISA_IVAR_PORTSIZE_0: 64247398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 0); 64347398Sdfr if (rle) 64447398Sdfr *result = rle->count; 64547398Sdfr else 64647398Sdfr *result = 0; 64747398Sdfr break; 64847398Sdfr 64947398Sdfr case ISA_IVAR_PORTSIZE_1: 65047398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 1); 65147398Sdfr if (rle) 65247398Sdfr *result = rle->count; 65347398Sdfr else 65447398Sdfr *result = 0; 65547398Sdfr break; 65647398Sdfr 65747398Sdfr case ISA_IVAR_MADDR_0: 65847398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 0); 65947398Sdfr if (rle) 66047398Sdfr *result = rle->start; 66147398Sdfr else 66247398Sdfr *result = -1; 66347398Sdfr break; 66447398Sdfr 66547398Sdfr case ISA_IVAR_MADDR_1: 66647398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 1); 66747398Sdfr if (rle) 66847398Sdfr *result = rle->start; 66947398Sdfr else 67047398Sdfr *result = -1; 67147398Sdfr break; 67247398Sdfr 67347398Sdfr case ISA_IVAR_MSIZE_0: 67447398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 0); 67547398Sdfr if (rle) 67647398Sdfr *result = rle->count; 67747398Sdfr else 67847398Sdfr *result = 0; 67947398Sdfr break; 68047398Sdfr 68147398Sdfr case ISA_IVAR_MSIZE_1: 68247398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 1); 68347398Sdfr if (rle) 68447398Sdfr *result = rle->count; 68547398Sdfr else 68647398Sdfr *result = 0; 68747398Sdfr break; 68847398Sdfr 68947398Sdfr case ISA_IVAR_IRQ_0: 69047398Sdfr rle = resource_list_find(rl, SYS_RES_IRQ, 0); 69147398Sdfr if (rle) 69247398Sdfr *result = rle->start; 69347398Sdfr else 69447398Sdfr *result = -1; 69547398Sdfr break; 69647398Sdfr 69747398Sdfr case ISA_IVAR_IRQ_1: 69847398Sdfr rle = resource_list_find(rl, SYS_RES_IRQ, 1); 69947398Sdfr if (rle) 70047398Sdfr *result = rle->start; 70147398Sdfr else 70247398Sdfr *result = -1; 70347398Sdfr break; 70447398Sdfr 70547398Sdfr case ISA_IVAR_DRQ_0: 70647398Sdfr rle = resource_list_find(rl, SYS_RES_DRQ, 0); 70747398Sdfr if (rle) 70847398Sdfr *result = rle->start; 70947398Sdfr else 71047398Sdfr *result = -1; 71147398Sdfr break; 71247398Sdfr 71347398Sdfr case ISA_IVAR_DRQ_1: 71447398Sdfr rle = resource_list_find(rl, SYS_RES_DRQ, 1); 71547398Sdfr if (rle) 71647398Sdfr *result = rle->start; 71747398Sdfr else 71847398Sdfr *result = -1; 71947398Sdfr break; 72047398Sdfr 72147613Sdfr case ISA_IVAR_VENDORID: 72247613Sdfr *result = idev->id_vendorid; 72347613Sdfr break; 72447613Sdfr 72547613Sdfr case ISA_IVAR_SERIAL: 72647613Sdfr *result = idev->id_serial; 72747613Sdfr break; 72847613Sdfr 72947613Sdfr case ISA_IVAR_LOGICALID: 73047613Sdfr *result = idev->id_logicalid; 73147613Sdfr break; 73247613Sdfr 73347613Sdfr case ISA_IVAR_COMPATID: 73447613Sdfr *result = idev->id_compatid; 73547613Sdfr break; 73647613Sdfr 73747613Sdfr default: 73847613Sdfr return ENOENT; 73947398Sdfr } 74047613Sdfr 74147613Sdfr return 0; 74247398Sdfr} 74347398Sdfr 74447398Sdfrstatic int 74547398Sdfrisa_write_ivar(device_t bus, device_t dev, 74647398Sdfr int index, uintptr_t value) 74747398Sdfr{ 74847398Sdfr struct isa_device* idev = DEVTOISA(dev); 74947398Sdfr 75047398Sdfr switch (index) { 75147398Sdfr case ISA_IVAR_PORT_0: 75247398Sdfr case ISA_IVAR_PORT_1: 75347398Sdfr case ISA_IVAR_PORTSIZE_0: 75447398Sdfr case ISA_IVAR_PORTSIZE_1: 75547398Sdfr case ISA_IVAR_MADDR_0: 75647398Sdfr case ISA_IVAR_MADDR_1: 75747398Sdfr case ISA_IVAR_MSIZE_0: 75847398Sdfr case ISA_IVAR_MSIZE_1: 75947398Sdfr case ISA_IVAR_IRQ_0: 76047398Sdfr case ISA_IVAR_IRQ_1: 76147398Sdfr case ISA_IVAR_DRQ_0: 76247398Sdfr case ISA_IVAR_DRQ_1: 76347398Sdfr return EINVAL; 76447398Sdfr 76547613Sdfr case ISA_IVAR_VENDORID: 76647613Sdfr idev->id_vendorid = value; 76747613Sdfr break; 76847613Sdfr 76947613Sdfr case ISA_IVAR_SERIAL: 77047613Sdfr idev->id_serial = value; 77147613Sdfr break; 77247613Sdfr 77347613Sdfr case ISA_IVAR_LOGICALID: 77447613Sdfr idev->id_logicalid = value; 77547613Sdfr break; 77647613Sdfr 77747613Sdfr case ISA_IVAR_COMPATID: 77847613Sdfr idev->id_compatid = value; 77947613Sdfr break; 78047613Sdfr 78147398Sdfr default: 78247398Sdfr return (ENOENT); 78347398Sdfr } 78447613Sdfr 78547398Sdfr return (0); 78647398Sdfr} 78747398Sdfr 78850769Sdfr/* 78950769Sdfr * Free any resources which the driver missed or which we were holding for 79050769Sdfr * it (see isa_probe_children). 79150769Sdfr */ 79250769Sdfrstatic void 79350769Sdfrisa_child_detached(device_t dev, device_t child) 79450769Sdfr{ 79550769Sdfr struct isa_device* idev = DEVTOISA(child); 79652174Sdfr struct resource_list *rl = &idev->id_resources; 79750769Sdfr struct resource_list_entry *rle; 79850769Sdfr 79962059Sdfr if (TAILQ_FIRST(&idev->id_configs)) { 80062059Sdfr /* 80162059Sdfr * Claim any unallocated resources to keep other 80262059Sdfr * devices from using them. 80362059Sdfr */ 80462059Sdfr SLIST_FOREACH(rle, rl, link) { 80562059Sdfr if (!rle->res) { 80662059Sdfr int rid = rle->rid; 80762059Sdfr resource_list_alloc(rl, dev, child, 80862059Sdfr rle->type, 80962059Sdfr &rid, 0, ~0, 1, 0); 81062059Sdfr } 81162059Sdfr } 81250769Sdfr } 81350769Sdfr} 81450769Sdfr 81553094Sdfrstatic void 81653094Sdfrisa_driver_added(device_t dev, driver_t *driver) 81753094Sdfr{ 81853094Sdfr device_t *children; 81953094Sdfr int nchildren, i; 82053094Sdfr 82153094Sdfr /* 82253094Sdfr * Don't do anything if drivers are dynamically 82353094Sdfr * added during autoconfiguration (cf. ymf724). 82453094Sdfr * since that would end up calling identify 82553094Sdfr * twice. 82653094Sdfr */ 82753094Sdfr if (!isa_running) 82853094Sdfr return; 82953094Sdfr 83053094Sdfr DEVICE_IDENTIFY(driver, dev); 83153094Sdfr if (device_get_children(dev, &children, &nchildren)) 83253094Sdfr return; 83353094Sdfr 83453094Sdfr for (i = 0; i < nchildren; i++) { 83553094Sdfr device_t child = children[i]; 83653094Sdfr struct isa_device *idev = DEVTOISA(child); 83753094Sdfr struct resource_list *rl = &idev->id_resources; 83853094Sdfr struct resource_list_entry *rle; 83953094Sdfr 84053094Sdfr if (device_get_state(child) != DS_NOTPRESENT) 84153094Sdfr continue; 84262059Sdfr if (!device_is_enabled(child)) 84362059Sdfr continue; 84453094Sdfr 84562059Sdfr /* 84662059Sdfr * Free resources which we were holding on behalf of 84762059Sdfr * the device. 84862059Sdfr */ 84962059Sdfr SLIST_FOREACH(rle, &idev->id_resources, link) { 85062059Sdfr if (rle->res) 85162059Sdfr resource_list_release(rl, dev, child, 85262059Sdfr rle->type, 85362059Sdfr rle->rid, 85462059Sdfr rle->res); 85562059Sdfr } 85662059Sdfr 85753094Sdfr if (TAILQ_FIRST(&idev->id_configs)) 85853094Sdfr if (!isa_assign_resources(child)) 85953094Sdfr continue; 86053094Sdfr 86153094Sdfr device_probe_and_attach(child); 86253094Sdfr 86353094Sdfr if (TAILQ_FIRST(&idev->id_configs)) { 86453094Sdfr /* 86553094Sdfr * Claim any unallocated resources to keep other 86653094Sdfr * devices from using them. 86753094Sdfr */ 86853094Sdfr SLIST_FOREACH(rle, rl, link) { 86953094Sdfr if (!rle->res) { 87053094Sdfr int rid = rle->rid; 87153094Sdfr resource_list_alloc(rl, dev, child, 87253094Sdfr rle->type, 87362059Sdfr &rid, 0, ~0, 1, 0); 87453094Sdfr } 87553094Sdfr } 87653094Sdfr } 87753094Sdfr } 87853094Sdfr 87953094Sdfr free(children, M_TEMP); 88053094Sdfr} 88153094Sdfr 88247398Sdfrstatic int 88347398Sdfrisa_set_resource(device_t dev, device_t child, int type, int rid, 88447398Sdfr u_long start, u_long count) 88547398Sdfr{ 88647398Sdfr struct isa_device* idev = DEVTOISA(child); 88747398Sdfr struct resource_list *rl = &idev->id_resources; 88847398Sdfr 88947398Sdfr if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY 89047398Sdfr && type != SYS_RES_IRQ && type != SYS_RES_DRQ) 89147398Sdfr return EINVAL; 89250769Sdfr if (rid < 0) 89347398Sdfr return EINVAL; 89450769Sdfr if (type == SYS_RES_IOPORT && rid >= ISA_NPORT) 89550769Sdfr return EINVAL; 89650769Sdfr if (type == SYS_RES_MEMORY && rid >= ISA_NMEM) 89750769Sdfr return EINVAL; 89850769Sdfr if (type == SYS_RES_IRQ && rid >= ISA_NIRQ) 89950769Sdfr return EINVAL; 90050769Sdfr if (type == SYS_RES_DRQ && rid >= ISA_NDRQ) 90150769Sdfr return EINVAL; 90247398Sdfr 90347398Sdfr resource_list_add(rl, type, rid, start, start + count - 1, count); 90447398Sdfr 90547398Sdfr return 0; 90647398Sdfr} 90747398Sdfr 90847398Sdfrstatic int 90947398Sdfrisa_get_resource(device_t dev, device_t child, int type, int rid, 91047398Sdfr u_long *startp, u_long *countp) 91147398Sdfr{ 91247398Sdfr struct isa_device* idev = DEVTOISA(child); 91347398Sdfr struct resource_list *rl = &idev->id_resources; 91447398Sdfr struct resource_list_entry *rle; 91547398Sdfr 91647398Sdfr rle = resource_list_find(rl, type, rid); 91747398Sdfr if (!rle) 91847398Sdfr return ENOENT; 91947398Sdfr 92053461Speter if (startp) 92153461Speter *startp = rle->start; 92253461Speter if (countp) 92353461Speter *countp = rle->count; 92447398Sdfr 92547398Sdfr return 0; 92647398Sdfr} 92747398Sdfr 92847613Sdfrstatic void 92947613Sdfrisa_delete_resource(device_t dev, device_t child, int type, int rid) 93047613Sdfr{ 93147613Sdfr struct isa_device* idev = DEVTOISA(child); 93247613Sdfr struct resource_list *rl = &idev->id_resources; 93347613Sdfr resource_list_delete(rl, type, rid); 93447613Sdfr} 93547613Sdfr 93650769Sdfrstatic int 93750769Sdfrisa_add_config(device_t dev, device_t child, 93850769Sdfr int priority, struct isa_config *config) 93950769Sdfr{ 94050769Sdfr struct isa_device* idev = DEVTOISA(child); 94150769Sdfr struct isa_config_entry *newice, *ice; 94250769Sdfr 94350769Sdfr newice = malloc(sizeof *ice, M_DEVBUF, M_NOWAIT); 94450769Sdfr if (!newice) 94550769Sdfr return ENOMEM; 94650769Sdfr 94750769Sdfr newice->ice_priority = priority; 94850769Sdfr newice->ice_config = *config; 94950769Sdfr 95050769Sdfr TAILQ_FOREACH(ice, &idev->id_configs, ice_link) { 95150769Sdfr if (ice->ice_priority > priority) 95250769Sdfr break; 95350769Sdfr } 95450769Sdfr if (ice) 95550769Sdfr TAILQ_INSERT_BEFORE(ice, newice, ice_link); 95650769Sdfr else 95750769Sdfr TAILQ_INSERT_TAIL(&idev->id_configs, newice, ice_link); 95850769Sdfr 95950769Sdfr return 0; 96050769Sdfr} 96150769Sdfr 96250769Sdfrstatic void 96350769Sdfrisa_set_config_callback(device_t dev, device_t child, 96450769Sdfr isa_config_cb *fn, void *arg) 96550769Sdfr{ 96650769Sdfr struct isa_device* idev = DEVTOISA(child); 96750769Sdfr 96850769Sdfr idev->id_config_cb = fn; 96950769Sdfr idev->id_config_arg = arg; 97050769Sdfr} 97150769Sdfr 97250769Sdfrstatic int 97350769Sdfrisa_pnp_probe(device_t dev, device_t child, struct isa_pnp_id *ids) 97450769Sdfr{ 97550769Sdfr struct isa_device* idev = DEVTOISA(child); 97650769Sdfr 97750769Sdfr if (!idev->id_vendorid) 97850769Sdfr return ENOENT; 97950769Sdfr 98050769Sdfr while (ids->ip_id) { 98150769Sdfr /* 98250769Sdfr * Really ought to support >1 compat id per device. 98350769Sdfr */ 98450769Sdfr if (idev->id_logicalid == ids->ip_id 98550769Sdfr || idev->id_compatid == ids->ip_id) { 98650910Sdfr if (ids->ip_desc) 98750910Sdfr device_set_desc(child, ids->ip_desc); 98850769Sdfr return 0; 98950769Sdfr } 99050769Sdfr ids++; 99150769Sdfr } 99250769Sdfr 99350769Sdfr return ENXIO; 99450769Sdfr} 99550769Sdfr 99647398Sdfrstatic device_method_t isa_methods[] = { 99747398Sdfr /* Device interface */ 99847398Sdfr DEVMETHOD(device_probe, isa_probe), 99947398Sdfr DEVMETHOD(device_attach, isa_attach), 100047398Sdfr DEVMETHOD(device_detach, bus_generic_detach), 100147398Sdfr DEVMETHOD(device_shutdown, bus_generic_shutdown), 100247398Sdfr DEVMETHOD(device_suspend, bus_generic_suspend), 100347398Sdfr DEVMETHOD(device_resume, bus_generic_resume), 100447398Sdfr 100547398Sdfr /* Bus interface */ 100647398Sdfr DEVMETHOD(bus_add_child, isa_add_child), 100747398Sdfr DEVMETHOD(bus_print_child, isa_print_child), 100862059Sdfr DEVMETHOD(bus_probe_nomatch, isa_probe_nomatch), 100947398Sdfr DEVMETHOD(bus_read_ivar, isa_read_ivar), 101047398Sdfr DEVMETHOD(bus_write_ivar, isa_write_ivar), 101150769Sdfr DEVMETHOD(bus_child_detached, isa_child_detached), 101253094Sdfr DEVMETHOD(bus_driver_added, isa_driver_added), 101347398Sdfr DEVMETHOD(bus_alloc_resource, isa_alloc_resource), 101447398Sdfr DEVMETHOD(bus_release_resource, isa_release_resource), 101547398Sdfr DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 101647398Sdfr DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 101747398Sdfr DEVMETHOD(bus_setup_intr, isa_setup_intr), 101847398Sdfr DEVMETHOD(bus_teardown_intr, isa_teardown_intr), 101952174Sdfr DEVMETHOD(bus_set_resource, isa_set_resource), 102052174Sdfr DEVMETHOD(bus_get_resource, isa_get_resource), 102152174Sdfr DEVMETHOD(bus_delete_resource, isa_delete_resource), 102247398Sdfr 102347398Sdfr /* ISA interface */ 102450769Sdfr DEVMETHOD(isa_add_config, isa_add_config), 102550769Sdfr DEVMETHOD(isa_set_config_callback, isa_set_config_callback), 102650769Sdfr DEVMETHOD(isa_pnp_probe, isa_pnp_probe), 102747398Sdfr 102847398Sdfr { 0, 0 } 102947398Sdfr}; 103047398Sdfr 103147398Sdfrstatic driver_t isa_driver = { 103247398Sdfr "isa", 103347398Sdfr isa_methods, 103447398Sdfr 1, /* no softc */ 103547398Sdfr}; 103647398Sdfr 103747398Sdfr/* 103847398Sdfr * ISA can be attached to a PCI-ISA bridge or directly to the nexus. 103947398Sdfr */ 104047398SdfrDRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0); 104147398Sdfr#ifdef __i386__ 104247398SdfrDRIVER_MODULE(isa, nexus, isa_driver, isa_devclass, 0, 0); 104347398Sdfr#endif 1044