isa_common.c revision 82863
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 82863 2001-09-03 09:45:41Z yokota $ 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; 38981401Sjulian struct isa_config *cfg; 39050769Sdfr 39181401Sjulian cfg = malloc(sizeof(struct isa_config), M_TEMP, M_NOWAIT|M_ZERO); 39281401Sjulian if (cfg == NULL) 39381401Sjulian return(0); 39450769Sdfr TAILQ_FOREACH(ice, &idev->id_configs, ice_link) { 39581401Sjulian if (!isa_find_memory(child, &ice->ice_config, cfg)) 39650769Sdfr continue; 39781401Sjulian if (!isa_find_port(child, &ice->ice_config, cfg)) 39850769Sdfr continue; 39981401Sjulian if (!isa_find_irq(child, &ice->ice_config, cfg)) 40050769Sdfr continue; 40181401Sjulian if (!isa_find_drq(child, &ice->ice_config, cfg)) 40250769Sdfr continue; 40350769Sdfr 40450769Sdfr /* 40550769Sdfr * A working configuration was found enable the device 40650769Sdfr * with this configuration. 40750769Sdfr */ 40850769Sdfr if (idev->id_config_cb) { 40950769Sdfr idev->id_config_cb(idev->id_config_arg, 41081401Sjulian cfg, 1); 41181401Sjulian free(cfg, M_TEMP); 41250769Sdfr return 1; 41350769Sdfr } 41450769Sdfr } 41550769Sdfr 41650769Sdfr /* 41750769Sdfr * Disable the device. 41850769Sdfr */ 41962987Sjhb bus_print_child_header(device_get_parent(child), child); 42062987Sjhb printf(" can't assign resources\n"); 42162987Sjhb if (bootverbose) 42262987Sjhb isa_print_child(device_get_parent(child), child); 42381401Sjulian bzero(cfg, sizeof (*cfg)); 42450769Sdfr if (idev->id_config_cb) 42581401Sjulian idev->id_config_cb(idev->id_config_arg, cfg, 0); 42650769Sdfr device_disable(child); 42750769Sdfr 42881401Sjulian free(cfg, M_TEMP); 42950769Sdfr return 0; 43050769Sdfr} 43150769Sdfr 43250769Sdfr/* 43350769Sdfr * Called after other devices have initialised to probe for isa devices. 43450769Sdfr */ 43550769Sdfrvoid 43650769Sdfrisa_probe_children(device_t dev) 43750769Sdfr{ 43850769Sdfr device_t *children; 43981401Sjulian struct isa_config *cfg; 44050769Sdfr int nchildren, i; 44150769Sdfr 44253094Sdfr /* 44353094Sdfr * Create all the children by calling driver's identify methods. 44453094Sdfr */ 44553094Sdfr bus_generic_probe(dev); 44653094Sdfr 44750769Sdfr if (device_get_children(dev, &children, &nchildren)) 44850769Sdfr return; 44950769Sdfr 45050769Sdfr /* 45151905Sdfr * First disable all pnp devices so that they don't get 45251905Sdfr * matched by legacy probes. 45351905Sdfr */ 45453094Sdfr if (bootverbose) 45553094Sdfr printf("isa_probe_children: disabling PnP devices\n"); 45681401Sjulian 45781401Sjulian cfg = malloc(sizeof(*cfg), M_TEMP, M_NOWAIT|M_ZERO); 45881401Sjulian if (cfg == NULL) { 45981401Sjulian free(children, M_TEMP); 46081401Sjulian return; 46181401Sjulian } 46281401Sjulian 46351905Sdfr for (i = 0; i < nchildren; i++) { 46451905Sdfr device_t child = children[i]; 46551905Sdfr struct isa_device *idev = DEVTOISA(child); 46651905Sdfr 46781401Sjulian bzero(cfg, sizeof(*cfg)); 46851905Sdfr if (idev->id_config_cb) 46981401Sjulian idev->id_config_cb(idev->id_config_arg, cfg, 0); 47051905Sdfr } 47151905Sdfr 47281401Sjulian free(cfg, M_TEMP); 47381401Sjulian 47451905Sdfr /* 47551905Sdfr * Next probe all non-pnp devices so that they claim their 47650769Sdfr * resources first. 47750769Sdfr */ 47853094Sdfr if (bootverbose) 47953094Sdfr printf("isa_probe_children: probing non-PnP devices\n"); 48050769Sdfr for (i = 0; i < nchildren; i++) { 48150769Sdfr device_t child = children[i]; 48250769Sdfr struct isa_device *idev = DEVTOISA(child); 48350769Sdfr 48450769Sdfr if (TAILQ_FIRST(&idev->id_configs)) 48550769Sdfr continue; 48650769Sdfr 48750769Sdfr device_probe_and_attach(child); 48850769Sdfr } 48950769Sdfr 49050769Sdfr /* 49151905Sdfr * Finally assign resource to pnp devices and probe them. 49250769Sdfr */ 49353094Sdfr if (bootverbose) 49453094Sdfr printf("isa_probe_children: probing PnP devices\n"); 49550769Sdfr for (i = 0; i < nchildren; i++) { 49650769Sdfr device_t child = children[i]; 49750769Sdfr struct isa_device* idev = DEVTOISA(child); 49850769Sdfr 49950769Sdfr if (!TAILQ_FIRST(&idev->id_configs)) 50050769Sdfr continue; 50150769Sdfr 50250769Sdfr if (isa_assign_resources(child)) { 50352174Sdfr struct resource_list *rl = &idev->id_resources; 50450769Sdfr struct resource_list_entry *rle; 50550769Sdfr 50650769Sdfr device_probe_and_attach(child); 50750769Sdfr 50850769Sdfr /* 50950769Sdfr * Claim any unallocated resources to keep other 51050769Sdfr * devices from using them. 51150769Sdfr */ 51252174Sdfr SLIST_FOREACH(rle, rl, link) { 51350769Sdfr if (!rle->res) { 51450769Sdfr int rid = rle->rid; 51552174Sdfr resource_list_alloc(rl, dev, child, 51650769Sdfr rle->type, 51750769Sdfr &rid, 51862059Sdfr 0, ~0, 1, 0); 51950769Sdfr } 52050769Sdfr } 52150769Sdfr } 52250769Sdfr } 52350769Sdfr 52450769Sdfr free(children, M_TEMP); 52553094Sdfr 52653094Sdfr isa_running = 1; 52750769Sdfr} 52850769Sdfr 52950769Sdfr/* 53047398Sdfr * Add a new child with default ivars. 53147398Sdfr */ 53247398Sdfrstatic device_t 53347578Sdfrisa_add_child(device_t dev, int order, const char *name, int unit) 53447398Sdfr{ 53554073Smdodd device_t child; 53647398Sdfr struct isa_device *idev; 53747398Sdfr 53869781Sdwmalone idev = malloc(sizeof(struct isa_device), M_ISADEV, M_NOWAIT | M_ZERO); 53947398Sdfr if (!idev) 54047398Sdfr return 0; 54147398Sdfr 54247398Sdfr resource_list_init(&idev->id_resources); 54350769Sdfr TAILQ_INIT(&idev->id_configs); 54447398Sdfr 54554073Smdodd child = device_add_child_ordered(dev, order, name, unit); 54654073Smdodd device_set_ivars(child, idev); 54754073Smdodd 54854073Smdodd return child; 54947398Sdfr} 55047398Sdfr 55162059Sdfrstatic int 55247398Sdfrisa_print_resources(struct resource_list *rl, const char *name, int type, 55352174Sdfr int count, const char *format) 55447398Sdfr{ 55550769Sdfr struct resource_list_entry *rle; 55650769Sdfr int printed; 55762059Sdfr int i, retval = 0;; 55847398Sdfr 55950769Sdfr printed = 0; 56052174Sdfr for (i = 0; i < count; i++) { 56150769Sdfr rle = resource_list_find(rl, type, i); 56250769Sdfr if (rle) { 56350769Sdfr if (printed == 0) 56462059Sdfr retval += printf(" %s ", name); 56550769Sdfr else if (printed > 0) 56662059Sdfr retval += printf(","); 56750769Sdfr printed++; 56862059Sdfr retval += printf(format, rle->start); 56950769Sdfr if (rle->count > 1) { 57062059Sdfr retval += printf("-"); 57162059Sdfr retval += printf(format, 57262059Sdfr rle->start + rle->count - 1); 57347398Sdfr } 57450769Sdfr } else if (i > 3) { 57550769Sdfr /* check the first few regardless */ 57650769Sdfr break; 57747398Sdfr } 57847398Sdfr } 57962059Sdfr return retval; 58047398Sdfr} 58147398Sdfr 58249195Smdoddstatic int 58362059Sdfrisa_print_all_resources(device_t dev) 58447398Sdfr{ 58547398Sdfr struct isa_device *idev = DEVTOISA(dev); 58647398Sdfr struct resource_list *rl = &idev->id_resources; 58749195Smdodd int retval = 0; 58847398Sdfr 58951052Sdfr if (SLIST_FIRST(rl) || device_get_flags(dev)) 59049195Smdodd retval += printf(" at"); 59147398Sdfr 59262059Sdfr retval += isa_print_resources(rl, "port", SYS_RES_IOPORT, 59362059Sdfr ISA_NPORT, "%#lx"); 59462059Sdfr retval += isa_print_resources(rl, "iomem", SYS_RES_MEMORY, 59562059Sdfr ISA_NMEM, "%#lx"); 59662059Sdfr retval += isa_print_resources(rl, "irq", SYS_RES_IRQ, 59762059Sdfr ISA_NIRQ, "%ld"); 59862059Sdfr retval += isa_print_resources(rl, "drq", SYS_RES_DRQ, 59962059Sdfr ISA_NDRQ, "%ld"); 60051052Sdfr if (device_get_flags(dev)) 60151052Sdfr retval += printf(" flags %#x", device_get_flags(dev)); 60247398Sdfr 60362059Sdfr return retval; 60462059Sdfr} 60562059Sdfr 60662059Sdfrstatic int 60762059Sdfrisa_print_child(device_t bus, device_t dev) 60862059Sdfr{ 60962059Sdfr int retval = 0; 61062059Sdfr 61162059Sdfr retval += bus_print_child_header(bus, dev); 61262059Sdfr retval += isa_print_all_resources(dev); 61349195Smdodd retval += bus_print_child_footer(bus, dev); 61449195Smdodd 61549195Smdodd return (retval); 61647398Sdfr} 61747398Sdfr 61862059Sdfrstatic void 61962059Sdfrisa_probe_nomatch(device_t dev, device_t child) 62062059Sdfr{ 62162987Sjhb if (bootverbose) { 62262987Sjhb bus_print_child_header(dev, child); 62362987Sjhb printf(" failed to probe"); 62462059Sdfr isa_print_all_resources(child); 62562987Sjhb bus_print_child_footer(dev, child); 62662987Sjhb } 62762059Sdfr 62862059Sdfr return; 62962059Sdfr} 63062059Sdfr 63147398Sdfrstatic int 63247398Sdfrisa_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result) 63347398Sdfr{ 63447398Sdfr struct isa_device* idev = DEVTOISA(dev); 63547398Sdfr struct resource_list *rl = &idev->id_resources; 63647398Sdfr struct resource_list_entry *rle; 63747398Sdfr 63847398Sdfr switch (index) { 63947398Sdfr case ISA_IVAR_PORT_0: 64047398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 0); 64147398Sdfr if (rle) 64247398Sdfr *result = rle->start; 64347398Sdfr else 64447398Sdfr *result = -1; 64547398Sdfr break; 64647398Sdfr 64747398Sdfr case ISA_IVAR_PORT_1: 64847398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 1); 64947398Sdfr if (rle) 65047398Sdfr *result = rle->start; 65147398Sdfr else 65247398Sdfr *result = -1; 65347398Sdfr break; 65447398Sdfr 65547398Sdfr case ISA_IVAR_PORTSIZE_0: 65647398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 0); 65747398Sdfr if (rle) 65847398Sdfr *result = rle->count; 65947398Sdfr else 66047398Sdfr *result = 0; 66147398Sdfr break; 66247398Sdfr 66347398Sdfr case ISA_IVAR_PORTSIZE_1: 66447398Sdfr rle = resource_list_find(rl, SYS_RES_IOPORT, 1); 66547398Sdfr if (rle) 66647398Sdfr *result = rle->count; 66747398Sdfr else 66847398Sdfr *result = 0; 66947398Sdfr break; 67047398Sdfr 67147398Sdfr case ISA_IVAR_MADDR_0: 67247398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 0); 67347398Sdfr if (rle) 67447398Sdfr *result = rle->start; 67547398Sdfr else 67647398Sdfr *result = -1; 67747398Sdfr break; 67847398Sdfr 67947398Sdfr case ISA_IVAR_MADDR_1: 68047398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 1); 68147398Sdfr if (rle) 68247398Sdfr *result = rle->start; 68347398Sdfr else 68447398Sdfr *result = -1; 68547398Sdfr break; 68647398Sdfr 68747398Sdfr case ISA_IVAR_MSIZE_0: 68847398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 0); 68947398Sdfr if (rle) 69047398Sdfr *result = rle->count; 69147398Sdfr else 69247398Sdfr *result = 0; 69347398Sdfr break; 69447398Sdfr 69547398Sdfr case ISA_IVAR_MSIZE_1: 69647398Sdfr rle = resource_list_find(rl, SYS_RES_MEMORY, 1); 69747398Sdfr if (rle) 69847398Sdfr *result = rle->count; 69947398Sdfr else 70047398Sdfr *result = 0; 70147398Sdfr break; 70247398Sdfr 70347398Sdfr case ISA_IVAR_IRQ_0: 70447398Sdfr rle = resource_list_find(rl, SYS_RES_IRQ, 0); 70547398Sdfr if (rle) 70647398Sdfr *result = rle->start; 70747398Sdfr else 70847398Sdfr *result = -1; 70947398Sdfr break; 71047398Sdfr 71147398Sdfr case ISA_IVAR_IRQ_1: 71247398Sdfr rle = resource_list_find(rl, SYS_RES_IRQ, 1); 71347398Sdfr if (rle) 71447398Sdfr *result = rle->start; 71547398Sdfr else 71647398Sdfr *result = -1; 71747398Sdfr break; 71847398Sdfr 71947398Sdfr case ISA_IVAR_DRQ_0: 72047398Sdfr rle = resource_list_find(rl, SYS_RES_DRQ, 0); 72147398Sdfr if (rle) 72247398Sdfr *result = rle->start; 72347398Sdfr else 72447398Sdfr *result = -1; 72547398Sdfr break; 72647398Sdfr 72747398Sdfr case ISA_IVAR_DRQ_1: 72847398Sdfr rle = resource_list_find(rl, SYS_RES_DRQ, 1); 72947398Sdfr if (rle) 73047398Sdfr *result = rle->start; 73147398Sdfr else 73247398Sdfr *result = -1; 73347398Sdfr break; 73447398Sdfr 73547613Sdfr case ISA_IVAR_VENDORID: 73647613Sdfr *result = idev->id_vendorid; 73747613Sdfr break; 73847613Sdfr 73947613Sdfr case ISA_IVAR_SERIAL: 74047613Sdfr *result = idev->id_serial; 74147613Sdfr break; 74247613Sdfr 74347613Sdfr case ISA_IVAR_LOGICALID: 74447613Sdfr *result = idev->id_logicalid; 74547613Sdfr break; 74647613Sdfr 74747613Sdfr case ISA_IVAR_COMPATID: 74847613Sdfr *result = idev->id_compatid; 74947613Sdfr break; 75047613Sdfr 75182863Syokota case ISA_IVAR_CONFIGATTR: 75282863Syokota *result = idev->id_config_attr; 75382863Syokota break; 75482863Syokota 75547613Sdfr default: 75647613Sdfr return ENOENT; 75747398Sdfr } 75847613Sdfr 75947613Sdfr return 0; 76047398Sdfr} 76147398Sdfr 76247398Sdfrstatic int 76347398Sdfrisa_write_ivar(device_t bus, device_t dev, 76447398Sdfr int index, uintptr_t value) 76547398Sdfr{ 76647398Sdfr struct isa_device* idev = DEVTOISA(dev); 76747398Sdfr 76847398Sdfr switch (index) { 76947398Sdfr case ISA_IVAR_PORT_0: 77047398Sdfr case ISA_IVAR_PORT_1: 77147398Sdfr case ISA_IVAR_PORTSIZE_0: 77247398Sdfr case ISA_IVAR_PORTSIZE_1: 77347398Sdfr case ISA_IVAR_MADDR_0: 77447398Sdfr case ISA_IVAR_MADDR_1: 77547398Sdfr case ISA_IVAR_MSIZE_0: 77647398Sdfr case ISA_IVAR_MSIZE_1: 77747398Sdfr case ISA_IVAR_IRQ_0: 77847398Sdfr case ISA_IVAR_IRQ_1: 77947398Sdfr case ISA_IVAR_DRQ_0: 78047398Sdfr case ISA_IVAR_DRQ_1: 78147398Sdfr return EINVAL; 78247398Sdfr 78347613Sdfr case ISA_IVAR_VENDORID: 78447613Sdfr idev->id_vendorid = value; 78547613Sdfr break; 78647613Sdfr 78747613Sdfr case ISA_IVAR_SERIAL: 78847613Sdfr idev->id_serial = value; 78947613Sdfr break; 79047613Sdfr 79147613Sdfr case ISA_IVAR_LOGICALID: 79247613Sdfr idev->id_logicalid = value; 79347613Sdfr break; 79447613Sdfr 79547613Sdfr case ISA_IVAR_COMPATID: 79647613Sdfr idev->id_compatid = value; 79747613Sdfr break; 79847613Sdfr 79982863Syokota case ISA_IVAR_CONFIGATTR: 80082863Syokota idev->id_config_attr = value; 80182863Syokota break; 80282863Syokota 80347398Sdfr default: 80447398Sdfr return (ENOENT); 80547398Sdfr } 80647613Sdfr 80747398Sdfr return (0); 80847398Sdfr} 80947398Sdfr 81050769Sdfr/* 81150769Sdfr * Free any resources which the driver missed or which we were holding for 81250769Sdfr * it (see isa_probe_children). 81350769Sdfr */ 81450769Sdfrstatic void 81550769Sdfrisa_child_detached(device_t dev, device_t child) 81650769Sdfr{ 81750769Sdfr struct isa_device* idev = DEVTOISA(child); 81852174Sdfr struct resource_list *rl = &idev->id_resources; 81950769Sdfr struct resource_list_entry *rle; 82050769Sdfr 82162059Sdfr if (TAILQ_FIRST(&idev->id_configs)) { 82262059Sdfr /* 82362059Sdfr * Claim any unallocated resources to keep other 82462059Sdfr * devices from using them. 82562059Sdfr */ 82662059Sdfr SLIST_FOREACH(rle, rl, link) { 82762059Sdfr if (!rle->res) { 82862059Sdfr int rid = rle->rid; 82962059Sdfr resource_list_alloc(rl, dev, child, 83062059Sdfr rle->type, 83162059Sdfr &rid, 0, ~0, 1, 0); 83262059Sdfr } 83362059Sdfr } 83450769Sdfr } 83550769Sdfr} 83650769Sdfr 83753094Sdfrstatic void 83853094Sdfrisa_driver_added(device_t dev, driver_t *driver) 83953094Sdfr{ 84053094Sdfr device_t *children; 84153094Sdfr int nchildren, i; 84253094Sdfr 84353094Sdfr /* 84453094Sdfr * Don't do anything if drivers are dynamically 84553094Sdfr * added during autoconfiguration (cf. ymf724). 84653094Sdfr * since that would end up calling identify 84753094Sdfr * twice. 84853094Sdfr */ 84953094Sdfr if (!isa_running) 85053094Sdfr return; 85153094Sdfr 85253094Sdfr DEVICE_IDENTIFY(driver, dev); 85353094Sdfr if (device_get_children(dev, &children, &nchildren)) 85453094Sdfr return; 85553094Sdfr 85653094Sdfr for (i = 0; i < nchildren; i++) { 85753094Sdfr device_t child = children[i]; 85853094Sdfr struct isa_device *idev = DEVTOISA(child); 85953094Sdfr struct resource_list *rl = &idev->id_resources; 86053094Sdfr struct resource_list_entry *rle; 86153094Sdfr 86253094Sdfr if (device_get_state(child) != DS_NOTPRESENT) 86353094Sdfr continue; 86462059Sdfr if (!device_is_enabled(child)) 86562059Sdfr continue; 86653094Sdfr 86762059Sdfr /* 86862059Sdfr * Free resources which we were holding on behalf of 86962059Sdfr * the device. 87062059Sdfr */ 87162059Sdfr SLIST_FOREACH(rle, &idev->id_resources, link) { 87262059Sdfr if (rle->res) 87362059Sdfr resource_list_release(rl, dev, child, 87462059Sdfr rle->type, 87562059Sdfr rle->rid, 87662059Sdfr rle->res); 87762059Sdfr } 87862059Sdfr 87953094Sdfr if (TAILQ_FIRST(&idev->id_configs)) 88053094Sdfr if (!isa_assign_resources(child)) 88153094Sdfr continue; 88253094Sdfr 88353094Sdfr device_probe_and_attach(child); 88453094Sdfr 88553094Sdfr if (TAILQ_FIRST(&idev->id_configs)) { 88653094Sdfr /* 88753094Sdfr * Claim any unallocated resources to keep other 88853094Sdfr * devices from using them. 88953094Sdfr */ 89053094Sdfr SLIST_FOREACH(rle, rl, link) { 89153094Sdfr if (!rle->res) { 89253094Sdfr int rid = rle->rid; 89353094Sdfr resource_list_alloc(rl, dev, child, 89453094Sdfr rle->type, 89562059Sdfr &rid, 0, ~0, 1, 0); 89653094Sdfr } 89753094Sdfr } 89853094Sdfr } 89953094Sdfr } 90053094Sdfr 90153094Sdfr free(children, M_TEMP); 90253094Sdfr} 90353094Sdfr 90447398Sdfrstatic int 90547398Sdfrisa_set_resource(device_t dev, device_t child, int type, int rid, 90647398Sdfr u_long start, u_long count) 90747398Sdfr{ 90847398Sdfr struct isa_device* idev = DEVTOISA(child); 90947398Sdfr struct resource_list *rl = &idev->id_resources; 91047398Sdfr 91147398Sdfr if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY 91247398Sdfr && type != SYS_RES_IRQ && type != SYS_RES_DRQ) 91347398Sdfr return EINVAL; 91450769Sdfr if (rid < 0) 91547398Sdfr return EINVAL; 91650769Sdfr if (type == SYS_RES_IOPORT && rid >= ISA_NPORT) 91750769Sdfr return EINVAL; 91850769Sdfr if (type == SYS_RES_MEMORY && rid >= ISA_NMEM) 91950769Sdfr return EINVAL; 92050769Sdfr if (type == SYS_RES_IRQ && rid >= ISA_NIRQ) 92150769Sdfr return EINVAL; 92250769Sdfr if (type == SYS_RES_DRQ && rid >= ISA_NDRQ) 92350769Sdfr return EINVAL; 92447398Sdfr 92547398Sdfr resource_list_add(rl, type, rid, start, start + count - 1, count); 92647398Sdfr 92747398Sdfr return 0; 92847398Sdfr} 92947398Sdfr 93069295Smdoddstatic struct resource_list * 93169295Smdoddisa_get_resource_list (device_t dev, device_t child) 93247398Sdfr{ 93347398Sdfr struct isa_device* idev = DEVTOISA(child); 93447398Sdfr struct resource_list *rl = &idev->id_resources; 93547398Sdfr 93669295Smdodd if (!rl) 93769295Smdodd return (NULL); 93847398Sdfr 93969295Smdodd return (rl); 94047398Sdfr} 94147398Sdfr 94250769Sdfrstatic int 94350769Sdfrisa_add_config(device_t dev, device_t child, 94450769Sdfr int priority, struct isa_config *config) 94550769Sdfr{ 94650769Sdfr struct isa_device* idev = DEVTOISA(child); 94750769Sdfr struct isa_config_entry *newice, *ice; 94850769Sdfr 94950769Sdfr newice = malloc(sizeof *ice, M_DEVBUF, M_NOWAIT); 95050769Sdfr if (!newice) 95150769Sdfr return ENOMEM; 95250769Sdfr 95350769Sdfr newice->ice_priority = priority; 95450769Sdfr newice->ice_config = *config; 95550769Sdfr 95650769Sdfr TAILQ_FOREACH(ice, &idev->id_configs, ice_link) { 95750769Sdfr if (ice->ice_priority > priority) 95850769Sdfr break; 95950769Sdfr } 96050769Sdfr if (ice) 96150769Sdfr TAILQ_INSERT_BEFORE(ice, newice, ice_link); 96250769Sdfr else 96350769Sdfr TAILQ_INSERT_TAIL(&idev->id_configs, newice, ice_link); 96450769Sdfr 96550769Sdfr return 0; 96650769Sdfr} 96750769Sdfr 96850769Sdfrstatic void 96950769Sdfrisa_set_config_callback(device_t dev, device_t child, 97050769Sdfr isa_config_cb *fn, void *arg) 97150769Sdfr{ 97250769Sdfr struct isa_device* idev = DEVTOISA(child); 97350769Sdfr 97450769Sdfr idev->id_config_cb = fn; 97550769Sdfr idev->id_config_arg = arg; 97650769Sdfr} 97750769Sdfr 97850769Sdfrstatic int 97950769Sdfrisa_pnp_probe(device_t dev, device_t child, struct isa_pnp_id *ids) 98050769Sdfr{ 98150769Sdfr struct isa_device* idev = DEVTOISA(child); 98250769Sdfr 98350769Sdfr if (!idev->id_vendorid) 98450769Sdfr return ENOENT; 98550769Sdfr 98682553Smsmith while (ids && ids->ip_id) { 98750769Sdfr /* 98850769Sdfr * Really ought to support >1 compat id per device. 98950769Sdfr */ 99050769Sdfr if (idev->id_logicalid == ids->ip_id 99150769Sdfr || idev->id_compatid == ids->ip_id) { 99250910Sdfr if (ids->ip_desc) 99350910Sdfr device_set_desc(child, ids->ip_desc); 99450769Sdfr return 0; 99550769Sdfr } 99650769Sdfr ids++; 99750769Sdfr } 99850769Sdfr 99950769Sdfr return ENXIO; 100050769Sdfr} 100150769Sdfr 100247398Sdfrstatic device_method_t isa_methods[] = { 100347398Sdfr /* Device interface */ 100447398Sdfr DEVMETHOD(device_probe, isa_probe), 100547398Sdfr DEVMETHOD(device_attach, isa_attach), 100647398Sdfr DEVMETHOD(device_detach, bus_generic_detach), 100747398Sdfr DEVMETHOD(device_shutdown, bus_generic_shutdown), 100847398Sdfr DEVMETHOD(device_suspend, bus_generic_suspend), 100947398Sdfr DEVMETHOD(device_resume, bus_generic_resume), 101047398Sdfr 101147398Sdfr /* Bus interface */ 101247398Sdfr DEVMETHOD(bus_add_child, isa_add_child), 101347398Sdfr DEVMETHOD(bus_print_child, isa_print_child), 101462059Sdfr DEVMETHOD(bus_probe_nomatch, isa_probe_nomatch), 101547398Sdfr DEVMETHOD(bus_read_ivar, isa_read_ivar), 101647398Sdfr DEVMETHOD(bus_write_ivar, isa_write_ivar), 101750769Sdfr DEVMETHOD(bus_child_detached, isa_child_detached), 101853094Sdfr DEVMETHOD(bus_driver_added, isa_driver_added), 101969295Smdodd DEVMETHOD(bus_setup_intr, isa_setup_intr), 102069295Smdodd DEVMETHOD(bus_teardown_intr, isa_teardown_intr), 102169295Smdodd 102269295Smdodd DEVMETHOD(bus_get_resource_list,isa_get_resource_list), 102347398Sdfr DEVMETHOD(bus_alloc_resource, isa_alloc_resource), 102447398Sdfr DEVMETHOD(bus_release_resource, isa_release_resource), 102569295Smdodd DEVMETHOD(bus_set_resource, isa_set_resource), 102669295Smdodd DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 102769295Smdodd DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), 102847398Sdfr DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 102947398Sdfr DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 103047398Sdfr 103147398Sdfr /* ISA interface */ 103250769Sdfr DEVMETHOD(isa_add_config, isa_add_config), 103350769Sdfr DEVMETHOD(isa_set_config_callback, isa_set_config_callback), 103450769Sdfr DEVMETHOD(isa_pnp_probe, isa_pnp_probe), 103547398Sdfr 103647398Sdfr { 0, 0 } 103747398Sdfr}; 103847398Sdfr 103947398Sdfrstatic driver_t isa_driver = { 104047398Sdfr "isa", 104147398Sdfr isa_methods, 104247398Sdfr 1, /* no softc */ 104347398Sdfr}; 104447398Sdfr 104547398Sdfr/* 104647398Sdfr * ISA can be attached to a PCI-ISA bridge or directly to the nexus. 104747398Sdfr */ 104847398SdfrDRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0); 104969942SmsmithDRIVER_MODULE(isa, eisab, isa_driver, isa_devclass, 0, 0); 105047398Sdfr#ifdef __i386__ 105147398SdfrDRIVER_MODULE(isa, nexus, isa_driver, isa_devclass, 0, 0); 105247398Sdfr#endif 1053