aha_isa.c revision 112782
1/* 2 * Product specific probe and attach routines for: 3 * Adaptec 154x. 4 * 5 * Derived from code written by: 6 * 7 * Copyright (c) 1998 Justin T. Gibbs 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions, and the following disclaimer, 15 * without modification, immediately at the beginning of the file. 16 * 2. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * $FreeBSD: head/sys/dev/aha/aha_isa.c 112782 2003-03-29 09:46:10Z mdodd $ 32 */ 33 34#include <sys/param.h> 35#include <sys/systm.h> 36#include <sys/kernel.h> 37 38#include <machine/bus_pio.h> 39#include <machine/bus.h> 40#include <machine/resource.h> 41#include <sys/module.h> 42#include <sys/bus.h> 43#include <sys/rman.h> 44 45#include <isa/isavar.h> 46 47#include <dev/aha/ahareg.h> 48 49#include <cam/scsi/scsi_all.h> 50 51static struct isa_pnp_id aha_ids[] = { 52 {ADP0100_PNP, "Adaptec 1540/1542 ISA SCSI"}, /* ADP0100 */ 53 {AHA1540_PNP, "Adaptec 1540/aha-1640/aha-1535"},/* ADP1542 */ 54 {AHA1542_PNP, "Adaptec 1542/aha-1535"}, /* ADP1542 */ 55 {AHA1542_PNPCOMPAT, "Adaptec 1542 compatible"}, /* PNP00A0 */ 56 {ICU0091_PNP, "Adaptec AHA-1540/1542 SCSI"}, /* ICU0091 */ 57 {0} 58}; 59 60/* 61 * Check if the device can be found at the port given 62 * and if so, set it up ready for further work 63 * as an argument, takes the isa_device structure from 64 * autoconf.c 65 */ 66static int 67aha_isa_probe(device_t dev) 68{ 69 /* 70 * find unit and check we have that many defined 71 */ 72 struct aha_softc **sc = device_get_softc(dev); 73 struct aha_softc *aha; 74 int port_index; 75 int max_port_index; 76 int error; 77 u_long port_start, port_count; 78 struct resource *port_res; 79 int port_rid; 80 int drq; 81 int irq; 82 83 aha = NULL; 84 85 /* Check isapnp ids */ 86 if (ISA_PNP_PROBE(device_get_parent(dev), dev, aha_ids) == ENXIO) 87 return (ENXIO); 88 89 error = bus_get_resource(dev, SYS_RES_IOPORT, 0, 90 &port_start, &port_count); 91 if (error != 0) 92 port_start = 0; 93 94 /* 95 * Bound our board search if the user has 96 * specified an exact port. 97 */ 98 aha_find_probe_range(port_start, &port_index, &max_port_index); 99 100 if (port_index < 0) 101 return ENXIO; 102 103 /* Attempt to find an adapter */ 104 for (;port_index <= max_port_index; port_index++) { 105 config_data_t config_data; 106 u_int ioport; 107 int error; 108 109 ioport = aha_iop_from_bio(port_index); 110 111 error = bus_set_resource(dev, SYS_RES_IOPORT, 0, 112 ioport, AHA_NREGS); 113 if (error) 114 return error; 115 116 port_rid = 0; 117 port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &port_rid, 118 0, ~0, AHA_NREGS, RF_ACTIVE); 119 if (!port_res) 120 continue; 121 122 /* Allocate a softc for use during probing */ 123 aha = aha_alloc(device_get_unit(dev), rman_get_bustag(port_res), 124 rman_get_bushandle(port_res)); 125 126 if (aha == NULL) { 127 bus_release_resource(dev, SYS_RES_IOPORT, port_rid, 128 port_res); 129 break; 130 } 131 132 /* See if there is really a card present */ 133 if (aha_probe(aha) || aha_fetch_adapter_info(aha)) { 134 aha_free(aha); 135 bus_release_resource(dev, SYS_RES_IOPORT, port_rid, 136 port_res); 137 continue; 138 } 139 140 /* 141 * Determine our IRQ, and DMA settings and 142 * export them to the configuration system. 143 */ 144 error = aha_cmd(aha, AOP_INQUIRE_CONFIG, NULL, /*parmlen*/0, 145 (u_int8_t*)&config_data, sizeof(config_data), 146 DEFAULT_CMD_TIMEOUT); 147 148 if (error != 0) { 149 printf("aha_isa_probe: Could not determine IRQ or DMA " 150 "settings for adapter at 0x%x. Failing probe\n", 151 ioport); 152 aha_free(aha); 153 bus_release_resource(dev, SYS_RES_IOPORT, port_rid, 154 port_res); 155 continue; 156 } 157 158 bus_release_resource(dev, SYS_RES_IOPORT, port_rid, port_res); 159 160 switch (config_data.dma_chan) { 161 case DMA_CHAN_5: 162 drq = 5; 163 break; 164 case DMA_CHAN_6: 165 drq = 6; 166 break; 167 case DMA_CHAN_7: 168 drq = 7; 169 break; 170 default: 171 printf("aha_isa_probe: Invalid DMA setting " 172 "detected for adapter at 0x%x. " 173 "Failing probe\n", ioport); 174 return (ENXIO); 175 } 176 error = bus_set_resource(dev, SYS_RES_DRQ, 0, drq, 1); 177 if (error) 178 return error; 179 180 irq = ffs(config_data.irq) + 8; 181 error = bus_set_resource(dev, SYS_RES_IRQ, 0, irq, 1); 182 if (error) 183 return error; 184 185 *sc = aha; 186 aha_unit++; 187 188 return (0); 189 } 190 191 return (ENXIO); 192} 193 194/* 195 * Attach all the sub-devices we can find 196 */ 197static int 198aha_isa_attach(device_t dev) 199{ 200 struct aha_softc **sc = device_get_softc(dev); 201 struct aha_softc *aha; 202 bus_dma_filter_t *filter; 203 void *filter_arg; 204 bus_addr_t lowaddr; 205 void *ih; 206 int error; 207 208 aha = *sc; 209 aha->portrid = 0; 210 aha->port = bus_alloc_resource(dev, SYS_RES_IOPORT, &aha->portrid, 211 0, ~0, AHA_NREGS, RF_ACTIVE); 212 if (!aha->port) { 213 device_printf(dev, "Unable to allocate I/O ports\n"); 214 return ENOMEM; 215 } 216 217 aha->irqrid = 0; 218 aha->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &aha->irqrid, 0, ~0, 1, 219 RF_ACTIVE); 220 if (!aha->irq) { 221 device_printf(dev, "Unable to allocate excluse use of irq\n"); 222 bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port); 223 return ENOMEM; 224 } 225 226 aha->drqrid = 0; 227 aha->drq = bus_alloc_resource(dev, SYS_RES_DRQ, &aha->drqrid, 0, ~0, 1, 228 RF_ACTIVE); 229 if (!aha->drq) { 230 device_printf(dev, "Unable to allocate drq\n"); 231 bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port); 232 bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq); 233 return ENOMEM; 234 } 235 236#if 0 /* is the drq ever unset? */ 237 if (dev->id_drq != -1) 238 isa_dmacascade(dev->id_drq); 239#endif 240 isa_dmacascade(rman_get_start(aha->drq)); 241 242 /* Allocate our parent dmatag */ 243 filter = NULL; 244 filter_arg = NULL; 245 lowaddr = BUS_SPACE_MAXADDR_24BIT; 246 247 if (bus_dma_tag_create( /* parent */ NULL, 248 /* alignemnt */ 1, 249 /* boundary */ 0, 250 /* lowaddr */ lowaddr, 251 /* highaddr */ BUS_SPACE_MAXADDR, 252 /* filter */ filter, 253 /* filterarg */ filter_arg, 254 /* maxsize */ BUS_SPACE_MAXSIZE_24BIT, 255 /* nsegments */ ~0, 256 /* maxsegsz */ BUS_SPACE_MAXSIZE_24BIT, 257 /* flags */ 0, 258 &aha->parent_dmat) != 0) { 259 aha_free(aha); 260 bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port); 261 bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq); 262 bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq); 263 return (ENOMEM); 264 } 265 266 if (aha_init(aha)) { 267 device_printf(dev, "init failed\n"); 268 aha_free(aha); 269 bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port); 270 bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq); 271 bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq); 272 return (ENOMEM); 273 } 274 275 error = aha_attach(aha); 276 if (error) { 277 device_printf(dev, "attach failed\n"); 278 aha_free(aha); 279 bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port); 280 bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq); 281 bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq); 282 return (error); 283 } 284 285 error = bus_setup_intr(dev, aha->irq, INTR_TYPE_CAM|INTR_ENTROPY, aha_intr, aha, 286 &ih); 287 if (error) { 288 device_printf(dev, "Unable to register interrupt handler\n"); 289 aha_free(aha); 290 bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port); 291 bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq); 292 bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq); 293 return (error); 294 } 295 296 return (0); 297} 298 299static int 300aha_isa_detach(device_t dev) 301{ 302 struct aha_softc *aha = *(struct aha_softc **) device_get_softc(dev); 303 int error; 304 305 error = bus_teardown_intr(dev, aha->irq, aha->ih); 306 if (error) { 307 device_printf(dev, "failed to unregister interrupt handler\n"); 308 } 309 310 bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port); 311 bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq); 312 bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq); 313 314 error = aha_detach(aha); 315 if (error) { 316 device_printf(dev, "detach failed\n"); 317 return (error); 318 } 319 aha_free(aha); 320 321 return (0); 322} 323 324static void 325aha_isa_identify(driver_t *driver, device_t parent) 326{ 327} 328 329static device_method_t aha_isa_methods[] = { 330 /* Device interface */ 331 DEVMETHOD(device_probe, aha_isa_probe), 332 DEVMETHOD(device_attach, aha_isa_attach), 333 DEVMETHOD(device_detach, aha_isa_detach), 334 DEVMETHOD(device_identify, aha_isa_identify), 335 336 { 0, 0 } 337}; 338 339static driver_t aha_isa_driver = { 340 "aha", 341 aha_isa_methods, 342 sizeof(struct aha_softc*), 343}; 344 345static devclass_t aha_devclass; 346 347DRIVER_MODULE(aha, isa, aha_isa_driver, aha_devclass, 0, 0); 348