| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
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: src/sys/dev/aha/aha_isa.c,v 1.17.2.1 2000/08/02 22:24:40 peter Exp $ | |
| 32 | */ | |
| 33 | ||
| 34 | #include <sys/param.h> | |
| 35 | #include <sys/systm.h> | |
| 36 | #include <sys/kernel.h> | |
| 984263bc MD |
37 | #include <sys/module.h> |
| 38 | #include <sys/bus.h> | |
| 39 | #include <sys/rman.h> | |
| 40 | ||
| 1f2de5d4 | 41 | #include <bus/isa/isavar.h> |
| 984263bc | 42 | |
| 1f2de5d4 | 43 | #include "ahareg.h" |
| 984263bc | 44 | |
| 1f2de5d4 | 45 | #include <bus/cam/scsi/scsi_all.h> |
| 984263bc MD |
46 | |
| 47 | static struct isa_pnp_id aha_ids[] = { | |
| 48 | {ADP0100_PNP, "Adaptec 1540/1542 ISA SCSI"}, /* ADP0100 */ | |
| 49 | {AHA1540_PNP, "Adaptec 1540/aha-1640/aha-1535"},/* ADP1542 */ | |
| 50 | {AHA1542_PNP, "Adaptec 1542/aha-1535"}, /* ADP1542 */ | |
| 51 | {AHA1542_PNPCOMPAT, "Adaptec 1542 compatible"}, /* PNP00A0 */ | |
| 52 | {ICU0091_PNP, "Adaptec AHA-1540/1542 SCSI"}, /* ICU0091 */ | |
| 53 | {0} | |
| 54 | }; | |
| 55 | ||
| 56 | /* | |
| 57 | * Check if the device can be found at the port given | |
| 58 | * and if so, set it up ready for further work | |
| 59 | * as an argument, takes the isa_device structure from | |
| 60 | * autoconf.c | |
| 61 | */ | |
| 62 | static int | |
| 63 | aha_isa_probe(device_t dev) | |
| 64 | { | |
| 65 | /* | |
| 66 | * find unit and check we have that many defined | |
| 67 | */ | |
| 68 | struct aha_softc **sc = device_get_softc(dev); | |
| 69 | struct aha_softc *aha; | |
| 70 | int port_index; | |
| 71 | int max_port_index; | |
| 72 | int error; | |
| 73 | u_long port_start, port_count; | |
| 74 | struct resource *port_res; | |
| 75 | int port_rid; | |
| 76 | int drq; | |
| 77 | int irq; | |
| 78 | ||
| 79 | aha = NULL; | |
| 80 | ||
| 81 | /* Check isapnp ids */ | |
| 82 | if (ISA_PNP_PROBE(device_get_parent(dev), dev, aha_ids) == ENXIO) | |
| 83 | return (ENXIO); | |
| 84 | ||
| 85 | error = bus_get_resource(dev, SYS_RES_IOPORT, 0, | |
| 86 | &port_start, &port_count); | |
| 87 | if (error != 0) | |
| 88 | port_start = 0; | |
| 89 | ||
| 90 | /* | |
| 91 | * Bound our board search if the user has | |
| 92 | * specified an exact port. | |
| 93 | */ | |
| 94 | aha_find_probe_range(port_start, &port_index, &max_port_index); | |
| 95 | ||
| 96 | if (port_index < 0) | |
| 97 | return ENXIO; | |
| 98 | ||
| 99 | /* Attempt to find an adapter */ | |
| 100 | for (;port_index <= max_port_index; port_index++) { | |
| 101 | config_data_t config_data; | |
| 102 | u_int ioport; | |
| 103 | int error; | |
| 104 | ||
| 105 | ioport = aha_iop_from_bio(port_index); | |
| 106 | ||
| 107 | error = bus_set_resource(dev, SYS_RES_IOPORT, 0, | |
| 108 | ioport, AHA_NREGS); | |
| 109 | if (error) | |
| 110 | return error; | |
| 111 | ||
| 112 | port_rid = 0; | |
| 113 | port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &port_rid, | |
| 114 | 0, ~0, AHA_NREGS, RF_ACTIVE); | |
| 115 | if (!port_res) | |
| 116 | continue; | |
| 117 | ||
| 118 | /* Allocate a softc for use during probing */ | |
| af77114a | 119 | aha = aha_alloc(dev, rman_get_bustag(port_res), |
| 984263bc MD |
120 | rman_get_bushandle(port_res)); |
| 121 | ||
| 122 | if (aha == NULL) { | |
| 123 | bus_release_resource(dev, SYS_RES_IOPORT, port_rid, | |
| 124 | port_res); | |
| 125 | break; | |
| 126 | } | |
| 127 | ||
| 128 | /* See if there is really a card present */ | |
| 129 | if (aha_probe(aha) || aha_fetch_adapter_info(aha)) { | |
| 130 | aha_free(aha); | |
| 131 | bus_release_resource(dev, SYS_RES_IOPORT, port_rid, | |
| 132 | port_res); | |
| 133 | continue; | |
| 134 | } | |
| 135 | ||
| 136 | /* | |
| 137 | * Determine our IRQ, and DMA settings and | |
| 138 | * export them to the configuration system. | |
| 139 | */ | |
| 140 | error = aha_cmd(aha, AOP_INQUIRE_CONFIG, NULL, /*parmlen*/0, | |
| 141 | (u_int8_t*)&config_data, sizeof(config_data), | |
| 142 | DEFAULT_CMD_TIMEOUT); | |
| 143 | ||
| 144 | if (error != 0) { | |
| e3869ec7 | 145 | kprintf("aha_isa_probe: Could not determine IRQ or DMA " |
| 984263bc MD |
146 | "settings for adapter at 0x%x. Failing probe\n", |
| 147 | ioport); | |
| 148 | aha_free(aha); | |
| 149 | bus_release_resource(dev, SYS_RES_IOPORT, port_rid, | |
| 150 | port_res); | |
| 151 | continue; | |
| 152 | } | |
| 153 | ||
| 154 | bus_release_resource(dev, SYS_RES_IOPORT, port_rid, port_res); | |
| 155 | ||
| 156 | switch (config_data.dma_chan) { | |
| 157 | case DMA_CHAN_5: | |
| 158 | drq = 5; | |
| 159 | break; | |
| 160 | case DMA_CHAN_6: | |
| 161 | drq = 6; | |
| 162 | break; | |
| 163 | case DMA_CHAN_7: | |
| 164 | drq = 7; | |
| 165 | break; | |
| 166 | default: | |
| e3869ec7 | 167 | kprintf("aha_isa_probe: Invalid DMA setting " |
| 984263bc MD |
168 | "detected for adapter at 0x%x. " |
| 169 | "Failing probe\n", ioport); | |
| 170 | return (ENXIO); | |
| 171 | } | |
| 172 | error = bus_set_resource(dev, SYS_RES_DRQ, 0, drq, 1); | |
| 173 | if (error) | |
| 174 | return error; | |
| 175 | ||
| 176 | irq = ffs(config_data.irq) + 8; | |
| 177 | error = bus_set_resource(dev, SYS_RES_IRQ, 0, irq, 1); | |
| 178 | if (error) | |
| 179 | return error; | |
| 180 | ||
| 181 | *sc = aha; | |
| 182 | aha_unit++; | |
| 183 | ||
| 184 | return (0); | |
| 185 | } | |
| 186 | ||
| 187 | return (ENXIO); | |
| 188 | } | |
| 189 | ||
| 190 | /* | |
| 191 | * Attach all the sub-devices we can find | |
| 192 | */ | |
| 193 | static int | |
| 194 | aha_isa_attach(device_t dev) | |
| 195 | { | |
| 196 | struct aha_softc **sc = device_get_softc(dev); | |
| 197 | struct aha_softc *aha; | |
| 198 | bus_dma_filter_t *filter; | |
| 199 | void *filter_arg; | |
| 200 | bus_addr_t lowaddr; | |
| 201 | void *ih; | |
| 202 | int error; | |
| 203 | ||
| 204 | aha = *sc; | |
| af77114a | 205 | aha->dev = dev; |
| 984263bc MD |
206 | aha->portrid = 0; |
| 207 | aha->port = bus_alloc_resource(dev, SYS_RES_IOPORT, &aha->portrid, | |
| 208 | 0, ~0, AHA_NREGS, RF_ACTIVE); | |
| 209 | if (!aha->port) { | |
| 210 | device_printf(dev, "Unable to allocate I/O ports\n"); | |
| 211 | return ENOMEM; | |
| 212 | } | |
| 213 | ||
| 214 | aha->irqrid = 0; | |
| 215 | aha->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &aha->irqrid, 0, ~0, 1, | |
| 216 | RF_ACTIVE); | |
| 217 | if (!aha->irq) { | |
| 218 | device_printf(dev, "Unable to allocate excluse use of irq\n"); | |
| 219 | bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port); | |
| 220 | return ENOMEM; | |
| 221 | } | |
| 222 | ||
| 223 | aha->drqrid = 0; | |
| 224 | aha->drq = bus_alloc_resource(dev, SYS_RES_DRQ, &aha->drqrid, 0, ~0, 1, | |
| 225 | RF_ACTIVE); | |
| 226 | if (!aha->drq) { | |
| 227 | device_printf(dev, "Unable to allocate drq\n"); | |
| 228 | bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port); | |
| 229 | bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq); | |
| 230 | return ENOMEM; | |
| 231 | } | |
| 232 | ||
| 233 | #if 0 /* is the drq ever unset? */ | |
| 234 | if (dev->id_drq != -1) | |
| 235 | isa_dmacascade(dev->id_drq); | |
| 236 | #endif | |
| 237 | isa_dmacascade(rman_get_start(aha->drq)); | |
| 238 | ||
| 239 | /* Allocate our parent dmatag */ | |
| 240 | filter = NULL; | |
| 241 | filter_arg = NULL; | |
| 242 | lowaddr = BUS_SPACE_MAXADDR_24BIT; | |
| 243 | ||
| 244 | if (bus_dma_tag_create(/*parent*/NULL, /*alignemnt*/1, /*boundary*/0, | |
| 245 | lowaddr, /*highaddr*/BUS_SPACE_MAXADDR, | |
| 246 | filter, filter_arg, | |
| 247 | /*maxsize*/BUS_SPACE_MAXSIZE_24BIT, | |
| 248 | /*nsegments*/BUS_SPACE_UNRESTRICTED, | |
| 249 | /*maxsegsz*/BUS_SPACE_MAXSIZE_24BIT, | |
| 250 | /*flags*/0, &aha->parent_dmat) != 0) { | |
| 984263bc MD |
251 | bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port); |
| 252 | bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq); | |
| 253 | bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq); | |
| 7c1a6b01 | 254 | aha_free(aha); |
| 984263bc MD |
255 | return (ENOMEM); |
| 256 | } | |
| 257 | ||
| 258 | if (aha_init(aha)) { | |
| 259 | device_printf(dev, "init failed\n"); | |
| 984263bc MD |
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); | |
| 7c1a6b01 | 263 | aha_free(aha); |
| 984263bc MD |
264 | return (ENOMEM); |
| 265 | } | |
| 266 | ||
| 267 | error = aha_attach(aha); | |
| 268 | if (error) { | |
| 269 | device_printf(dev, "attach failed\n"); | |
| 984263bc MD |
270 | bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port); |
| 271 | bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq); | |
| 272 | bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq); | |
| 7c1a6b01 | 273 | aha_free(aha); |
| 984263bc MD |
274 | return (error); |
| 275 | } | |
| 276 | ||
| ee61f228 | 277 | error = bus_setup_intr(dev, aha->irq, 0, aha_intr, aha, &ih, NULL); |
| 984263bc MD |
278 | if (error) { |
| 279 | device_printf(dev, "Unable to register interrupt handler\n"); | |
| 984263bc MD |
280 | bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port); |
| 281 | bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq); | |
| 282 | bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq); | |
| 7c1a6b01 | 283 | aha_free(aha); |
| 984263bc MD |
284 | return (error); |
| 285 | } | |
| 286 | ||
| 287 | return (0); | |
| 288 | } | |
| 289 | ||
| 290 | static int | |
| 291 | aha_isa_detach(device_t dev) | |
| 292 | { | |
| 293 | struct aha_softc *aha = *(struct aha_softc **) device_get_softc(dev); | |
| 294 | int error; | |
| 295 | ||
| 296 | error = bus_teardown_intr(dev, aha->irq, aha->ih); | |
| 297 | if (error) { | |
| 298 | device_printf(dev, "failed to unregister interrupt handler\n"); | |
| 299 | } | |
| 300 | ||
| 301 | bus_release_resource(dev, SYS_RES_IOPORT, aha->portrid, aha->port); | |
| 302 | bus_release_resource(dev, SYS_RES_IRQ, aha->irqrid, aha->irq); | |
| 303 | bus_release_resource(dev, SYS_RES_DRQ, aha->drqrid, aha->drq); | |
| 304 | ||
| 305 | error = aha_detach(aha); | |
| 306 | if (error) { | |
| 307 | device_printf(dev, "detach failed\n"); | |
| 308 | return (error); | |
| 309 | } | |
| 310 | aha_free(aha); | |
| 311 | ||
| 312 | return (0); | |
| 313 | } | |
| 314 | ||
| 984263bc MD |
315 | static device_method_t aha_isa_methods[] = { |
| 316 | /* Device interface */ | |
| 317 | DEVMETHOD(device_probe, aha_isa_probe), | |
| 318 | DEVMETHOD(device_attach, aha_isa_attach), | |
| 319 | DEVMETHOD(device_detach, aha_isa_detach), | |
| 984263bc MD |
320 | |
| 321 | { 0, 0 } | |
| 322 | }; | |
| 323 | ||
| 324 | static driver_t aha_isa_driver = { | |
| 325 | "aha", | |
| 326 | aha_isa_methods, | |
| 327 | sizeof(struct aha_softc*), | |
| 328 | }; | |
| 329 | ||
| 330 | static devclass_t aha_devclass; | |
| 331 | ||
| aa2b9d05 | 332 | DRIVER_MODULE(aha, isa, aha_isa_driver, aha_devclass, NULL, NULL); |