| 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> |
| 37 | #include <sys/module.h> |
| 38 | #include <sys/bus.h> |
| 39 | #include <sys/rman.h> |
| 40 | |
| 41 | #include <bus/isa/isavar.h> |
| 42 | |
| 43 | #include "ahareg.h" |
| 44 | |
| 45 | #include <bus/cam/scsi/scsi_all.h> |
| 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 */ |
| 119 | aha = aha_alloc(dev, rman_get_bustag(port_res), |
| 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) { |
| 145 | kprintf("aha_isa_probe: Could not determine IRQ or DMA " |
| 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: |
| 167 | kprintf("aha_isa_probe: Invalid DMA setting " |
| 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; |
| 205 | aha->dev = dev; |
| 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) { |
| 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); |
| 254 | aha_free(aha); |
| 255 | return (ENOMEM); |
| 256 | } |
| 257 | |
| 258 | if (aha_init(aha)) { |
| 259 | device_printf(dev, "init failed\n"); |
| 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 | aha_free(aha); |
| 264 | return (ENOMEM); |
| 265 | } |
| 266 | |
| 267 | error = aha_attach(aha); |
| 268 | if (error) { |
| 269 | device_printf(dev, "attach failed\n"); |
| 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); |
| 273 | aha_free(aha); |
| 274 | return (error); |
| 275 | } |
| 276 | |
| 277 | error = bus_setup_intr(dev, aha->irq, 0, aha_intr, aha, &ih, NULL); |
| 278 | if (error) { |
| 279 | device_printf(dev, "Unable to register interrupt handler\n"); |
| 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); |
| 283 | aha_free(aha); |
| 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 | |
| 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), |
| 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 | |
| 332 | DRIVER_MODULE(aha, isa, aha_isa_driver, aha_devclass, NULL, NULL); |