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