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