Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / dev / raid / mly / mly_pci.c
1 /*-
2  * Copyright (c) 2000, 2001 Michael Smith
3  * Copyright (c) 2000 BSDi
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  *      $FreeBSD: src/sys/dev/mly/mly_pci.c,v 1.1.2.2 2001/03/05 20:17:24 msmith Exp $
28  */
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/malloc.h>
33 #include <sys/kernel.h>
34
35 #include <sys/bus.h>
36 #include <sys/conf.h>
37 #include <sys/devicestat.h>
38 #include <sys/disk.h>
39
40 #include <machine/bus_memio.h>
41 #include <machine/bus.h>
42 #include <machine/resource.h>
43 #include <sys/rman.h>
44
45 #include <pci/pcireg.h>
46 #include <pci/pcivar.h>
47
48 #include <dev/mly/mlyreg.h>
49 #include <dev/mly/mlyio.h>
50 #include <dev/mly/mlyvar.h>
51
52 static int      mly_pci_probe(device_t dev);
53 static int      mly_pci_attach(device_t dev);
54 static int      mly_pci_detach(device_t dev);
55 static int      mly_pci_shutdown(device_t dev);
56 static int      mly_pci_suspend(device_t dev); 
57 static int      mly_pci_resume(device_t dev);
58 static void     mly_pci_intr(void *arg);
59
60 static int      mly_sg_map(struct mly_softc *sc);
61 static void     mly_sg_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error);
62 static int      mly_mmbox_map(struct mly_softc *sc);
63 static void     mly_mmbox_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error);
64
65 static device_method_t mly_methods[] = {
66     /* Device interface */
67     DEVMETHOD(device_probe,     mly_pci_probe),
68     DEVMETHOD(device_attach,    mly_pci_attach),
69     DEVMETHOD(device_detach,    mly_pci_detach),
70     DEVMETHOD(device_shutdown,  mly_pci_shutdown),
71     DEVMETHOD(device_suspend,   mly_pci_suspend),
72     DEVMETHOD(device_resume,    mly_pci_resume),
73     { 0, 0 }
74 };
75
76 static driver_t mly_pci_driver = {
77         "mly",
78         mly_methods,
79         sizeof(struct mly_softc)
80 };
81
82 static devclass_t       mly_devclass;
83 DRIVER_MODULE(mly, pci, mly_pci_driver, mly_devclass, 0, 0);
84
85 struct mly_ident
86 {
87     u_int16_t           vendor;
88     u_int16_t           device;
89     u_int16_t           subvendor;
90     u_int16_t           subdevice;
91     int                 hwif;
92     char                *desc;
93 } mly_identifiers[] = {
94     {0x1069, 0xba56, 0x1069, 0x0040, MLY_HWIF_STRONGARM, "Mylex eXtremeRAID 2000"},
95     {0x1069, 0xba56, 0x1069, 0x0030, MLY_HWIF_STRONGARM, "Mylex eXtremeRAID 3000"},
96     {0x1069, 0x0050, 0x1069, 0x0050, MLY_HWIF_I960RX,    "Mylex AcceleRAID 352"},
97     {0x1069, 0x0050, 0x1069, 0x0052, MLY_HWIF_I960RX,    "Mylex AcceleRAID 170"},
98     {0x1069, 0x0050, 0x1069, 0x0054, MLY_HWIF_I960RX,    "Mylex AcceleRAID 160"},
99     {0, 0, 0, 0, 0, 0}
100 };
101
102 /********************************************************************************
103  ********************************************************************************
104                                                                     Bus Interface
105  ********************************************************************************
106  ********************************************************************************/
107
108 static int
109 mly_pci_probe(device_t dev)
110 {
111     struct mly_ident    *m;
112
113     debug_called(1);
114
115     for (m = mly_identifiers; m->vendor != 0; m++) {
116         if ((m->vendor == pci_get_vendor(dev)) &&
117             (m->device == pci_get_device(dev)) &&
118             ((m->subvendor == 0) || ((m->subvendor == pci_get_subvendor(dev)) &&
119                                      (m->subdevice == pci_get_subdevice(dev))))) {
120             
121             device_set_desc(dev, m->desc);
122             return(-10);        /* allow room to be overridden */
123         }
124     }
125     return(ENXIO);
126 }
127
128 static int
129 mly_pci_attach(device_t dev)
130 {
131     struct mly_softc    *sc;
132     int                 i, error;
133     u_int32_t           command;
134
135     debug_called(1);
136
137     /*
138      * Initialise softc.
139      */
140     sc = device_get_softc(dev);
141     bzero(sc, sizeof(*sc));
142     sc->mly_dev = dev;
143
144 #ifdef MLY_DEBUG
145     if (device_get_unit(sc->mly_dev) == 0)
146         mly_softc0 = sc;
147 #endif    
148
149     /* assume failure is 'not configured' */
150     error = ENXIO;
151
152     /* 
153      * Verify that the adapter is correctly set up in PCI space.
154      */
155     command = pci_read_config(sc->mly_dev, PCIR_COMMAND, 2);
156     command |= PCIM_CMD_BUSMASTEREN;
157     pci_write_config(dev, PCIR_COMMAND, command, 2);
158     command = pci_read_config(sc->mly_dev, PCIR_COMMAND, 2);
159     if (!(command & PCIM_CMD_BUSMASTEREN)) {
160         mly_printf(sc, "can't enable busmaster feature\n");
161         goto fail;
162     }
163     if ((command & PCIM_CMD_MEMEN) == 0) {
164         mly_printf(sc, "memory window not available\n");
165         goto fail;
166     }
167
168     /*
169      * Allocate the PCI register window.
170      */
171     sc->mly_regs_rid = PCIR_MAPS;       /* first base address register */
172     if ((sc->mly_regs_resource = bus_alloc_resource(sc->mly_dev, SYS_RES_MEMORY, &sc->mly_regs_rid, 
173                                                     0, ~0, 1, RF_ACTIVE)) == NULL) {
174         mly_printf(sc, "can't allocate register window\n");
175         goto fail;
176     }
177     sc->mly_btag = rman_get_bustag(sc->mly_regs_resource);
178     sc->mly_bhandle = rman_get_bushandle(sc->mly_regs_resource);
179
180     /* 
181      * Allocate and connect our interrupt.
182      */
183     sc->mly_irq_rid = 0;
184     if ((sc->mly_irq = bus_alloc_resource(sc->mly_dev, SYS_RES_IRQ, &sc->mly_irq_rid, 
185                                           0, ~0, 1, RF_SHAREABLE | RF_ACTIVE)) == NULL) {
186         mly_printf(sc, "can't allocate interrupt\n");
187         goto fail;
188     }
189     if (bus_setup_intr(sc->mly_dev, sc->mly_irq, INTR_TYPE_CAM,  mly_pci_intr, sc, &sc->mly_intr)) {
190         mly_printf(sc, "can't set up interrupt\n");
191         goto fail;
192     }
193
194     /* assume failure is 'out of memory' */
195     error = ENOMEM;
196
197     /*
198      * Allocate the parent bus DMA tag appropriate for our PCI interface.
199      * 
200      * Note that all of these controllers are 64-bit capable.
201      */
202     if (bus_dma_tag_create(NULL,                        /* parent */
203                            1, 0,                        /* alignment, boundary */
204                            BUS_SPACE_MAXADDR_32BIT,     /* lowaddr */
205                            BUS_SPACE_MAXADDR,           /* highaddr */
206                            NULL, NULL,                  /* filter, filterarg */
207                            MAXBSIZE, MLY_MAXSGENTRIES,  /* maxsize, nsegments */
208                            BUS_SPACE_MAXSIZE_32BIT,     /* maxsegsize */
209                            BUS_DMA_ALLOCNOW,            /* flags */
210                            &sc->mly_parent_dmat)) {
211         mly_printf(sc, "can't allocate parent DMA tag\n");
212         goto fail;
213     }
214
215     /*
216      * Create DMA tag for mapping buffers into controller-addressable space.
217      */
218     if (bus_dma_tag_create(sc->mly_parent_dmat,         /* parent */
219                            1, 0,                        /* alignment, boundary */
220                            BUS_SPACE_MAXADDR,           /* lowaddr */
221                            BUS_SPACE_MAXADDR,           /* highaddr */
222                            NULL, NULL,                  /* filter, filterarg */
223                            MAXBSIZE, MLY_MAXSGENTRIES,  /* maxsize, nsegments */
224                            BUS_SPACE_MAXSIZE_32BIT,     /* maxsegsize */
225                            0,                           /* flags */
226                            &sc->mly_buffer_dmat)) {
227         mly_printf(sc, "can't allocate buffer DMA tag\n");
228         goto fail;
229     }
230
231     /*
232      * Initialise the DMA tag for command packets.
233      */
234     if (bus_dma_tag_create(sc->mly_parent_dmat,         /* parent */
235                            1, 0,                        /* alignment, boundary */
236                            BUS_SPACE_MAXADDR,           /* lowaddr */
237                            BUS_SPACE_MAXADDR,           /* highaddr */
238                            NULL, NULL,                  /* filter, filterarg */
239                            sizeof(union mly_command_packet) * MLY_MAXCOMMANDS, 1,       /* maxsize, nsegments */
240                            BUS_SPACE_MAXSIZE_32BIT,     /* maxsegsize */
241                            0,                           /* flags */
242                            &sc->mly_packet_dmat)) {
243         mly_printf(sc, "can't allocate command packet DMA tag\n");
244         goto fail;
245     }
246
247     /* 
248      * Detect the hardware interface version 
249      */
250     for (i = 0; mly_identifiers[i].vendor != 0; i++) {
251         if ((mly_identifiers[i].vendor == pci_get_vendor(dev)) &&
252             (mly_identifiers[i].device == pci_get_device(dev))) {
253             sc->mly_hwif = mly_identifiers[i].hwif;
254             switch(sc->mly_hwif) {
255             case MLY_HWIF_I960RX:
256                 debug(2, "set hardware up for i960RX");
257                 sc->mly_doorbell_true = 0x00;
258                 sc->mly_command_mailbox =  MLY_I960RX_COMMAND_MAILBOX;
259                 sc->mly_status_mailbox =   MLY_I960RX_STATUS_MAILBOX;
260                 sc->mly_idbr =             MLY_I960RX_IDBR;
261                 sc->mly_odbr =             MLY_I960RX_ODBR;
262                 sc->mly_error_status =     MLY_I960RX_ERROR_STATUS;
263                 sc->mly_interrupt_status = MLY_I960RX_INTERRUPT_STATUS;
264                 sc->mly_interrupt_mask =   MLY_I960RX_INTERRUPT_MASK;
265                 break;
266             case MLY_HWIF_STRONGARM:
267                 debug(2, "set hardware up for StrongARM");
268                 sc->mly_doorbell_true = 0xff;           /* doorbell 'true' is 0 */
269                 sc->mly_command_mailbox =  MLY_STRONGARM_COMMAND_MAILBOX;
270                 sc->mly_status_mailbox =   MLY_STRONGARM_STATUS_MAILBOX;
271                 sc->mly_idbr =             MLY_STRONGARM_IDBR;
272                 sc->mly_odbr =             MLY_STRONGARM_ODBR;
273                 sc->mly_error_status =     MLY_STRONGARM_ERROR_STATUS;
274                 sc->mly_interrupt_status = MLY_STRONGARM_INTERRUPT_STATUS;
275                 sc->mly_interrupt_mask =   MLY_STRONGARM_INTERRUPT_MASK;
276                 break;
277             }
278             break;
279         }
280     }
281
282     /*
283      * Create the scatter/gather mappings.
284      */
285     if ((error = mly_sg_map(sc)))
286         goto fail;
287
288     /*
289      * Allocate and map the memory mailbox
290      */
291     if ((error = mly_mmbox_map(sc)))
292         goto fail;
293
294     /*
295      * Do bus-independent initialisation.
296      */
297     if ((error = mly_attach(sc)))
298         goto fail;
299     
300     return(0);
301             
302 fail:
303     mly_free(sc);
304     return(error);
305 }
306
307 /********************************************************************************
308  * Disconnect from the controller completely, in preparation for unload.
309  */
310 static int
311 mly_pci_detach(device_t dev)
312 {
313     struct mly_softc    *sc = device_get_softc(dev);
314     int                 error;
315
316     debug_called(1);
317
318     if (sc->mly_state & MLY_STATE_OPEN)
319         return(EBUSY);
320
321     if ((error = mly_pci_shutdown(dev)))
322         return(error);
323
324     mly_free(sc);
325
326     return(0);
327 }
328
329 /********************************************************************************
330  * Bring the controller down to a dormant state and detach all child devices.
331  *
332  * This function is called before detach or system shutdown.
333  *
334  * Note that we can assume that the camq on the controller is empty, as we won't
335  * allow shutdown if any device is open.
336  */
337 static int
338 mly_pci_shutdown(device_t dev)
339 {
340     struct mly_softc    *sc = device_get_softc(dev);
341
342     debug_called(1);
343
344     mly_detach(sc);
345     return(0);
346 }
347
348 /********************************************************************************
349  * Bring the controller to a quiescent state, ready for system suspend.
350  *
351  * We can't assume that the controller is not active at this point, so we need
352  * to mask interrupts.
353  */
354 static int
355 mly_pci_suspend(device_t dev)
356 {
357     struct mly_softc    *sc = device_get_softc(dev);
358     int                 s;
359
360     debug_called(1);
361     s = splcam();
362     mly_detach(sc);
363     splx(s);
364     return(0);
365 }
366
367 /********************************************************************************
368  * Bring the controller back to a state ready for operation.
369  */
370 static int
371 mly_pci_resume(device_t dev)
372 {
373     struct mly_softc    *sc = device_get_softc(dev);
374
375     debug_called(1);
376     sc->mly_state &= ~MLY_STATE_SUSPEND;
377     MLY_UNMASK_INTERRUPTS(sc);
378     return(0);
379 }
380
381 /*******************************************************************************
382  * Take an interrupt, or be poked by other code to look for interrupt-worthy
383  * status.
384  */
385 static void
386 mly_pci_intr(void *arg)
387 {
388     struct mly_softc    *sc = (struct mly_softc *)arg;
389
390     debug_called(3);
391
392     /* collect finished commands, queue anything waiting */
393     mly_done(sc);
394 };
395
396 /********************************************************************************
397  ********************************************************************************
398                                                 Bus-dependant Resource Management
399  ********************************************************************************
400  ********************************************************************************/
401
402 /********************************************************************************
403  * Allocate memory for the scatter/gather tables
404  */
405 static int
406 mly_sg_map(struct mly_softc *sc)
407 {
408     size_t      segsize;
409
410     debug_called(1);
411
412     /*
413      * Create a single tag describing a region large enough to hold all of
414      * the s/g lists we will need.
415      */
416     segsize = sizeof(struct mly_sg_entry) * MLY_MAXCOMMANDS * MLY_MAXSGENTRIES;
417     if (bus_dma_tag_create(sc->mly_parent_dmat,         /* parent */
418                            1, 0,                        /* alignment, boundary */
419                            BUS_SPACE_MAXADDR,           /* lowaddr */
420                            BUS_SPACE_MAXADDR,           /* highaddr */
421                            NULL, NULL,                  /* filter, filterarg */
422                            segsize, 1,                  /* maxsize, nsegments */
423                            BUS_SPACE_MAXSIZE_32BIT,     /* maxsegsize */
424                            0,                           /* flags */
425                            &sc->mly_sg_dmat)) {
426         mly_printf(sc, "can't allocate scatter/gather DMA tag\n");
427         return(ENOMEM);
428     }
429
430     /*
431      * Allocate enough s/g maps for all commands and permanently map them into
432      * controller-visible space.
433      *  
434      * XXX this assumes we can get enough space for all the s/g maps in one 
435      * contiguous slab.
436      */
437     if (bus_dmamem_alloc(sc->mly_sg_dmat, (void **)&sc->mly_sg_table, BUS_DMA_NOWAIT, &sc->mly_sg_dmamap)) {
438         mly_printf(sc, "can't allocate s/g table\n");
439         return(ENOMEM);
440     }
441     bus_dmamap_load(sc->mly_sg_dmat, sc->mly_sg_dmamap, sc->mly_sg_table, segsize, mly_sg_map_helper, sc, 0);
442     return(0);
443 }
444
445 /********************************************************************************
446  * Save the physical address of the base of the s/g table.
447  */
448 static void
449 mly_sg_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
450 {
451     struct mly_softc    *sc = (struct mly_softc *)arg;
452
453     debug_called(2);
454
455     /* save base of s/g table's address in bus space */
456     sc->mly_sg_busaddr = segs->ds_addr;
457 }
458
459 /********************************************************************************
460  * Allocate memory for the memory-mailbox interface
461  */
462 static int
463 mly_mmbox_map(struct mly_softc *sc)
464 {
465
466     /*
467      * Create a DMA tag for a single contiguous region large enough for the
468      * memory mailbox structure.
469      */
470     if (bus_dma_tag_create(sc->mly_parent_dmat,         /* parent */
471                            1, 0,                        /* alignment, boundary */
472                            BUS_SPACE_MAXADDR,           /* lowaddr */
473                            BUS_SPACE_MAXADDR,           /* highaddr */
474                            NULL, NULL,                  /* filter, filterarg */
475                            sizeof(struct mly_mmbox), 1, /* maxsize, nsegments */
476                            BUS_SPACE_MAXSIZE_32BIT,     /* maxsegsize */
477                            0,                           /* flags */
478                            &sc->mly_mmbox_dmat)) {
479         mly_printf(sc, "can't allocate memory mailbox DMA tag\n");
480         return(ENOMEM);
481     }
482
483     /*
484      * Allocate the buffer
485      */
486     if (bus_dmamem_alloc(sc->mly_mmbox_dmat, (void **)&sc->mly_mmbox, BUS_DMA_NOWAIT, &sc->mly_mmbox_dmamap)) {
487         mly_printf(sc, "can't allocate memory mailbox\n");
488         return(ENOMEM);
489     }
490     bus_dmamap_load(sc->mly_mmbox_dmat, sc->mly_mmbox_dmamap, sc->mly_mmbox, sizeof(struct mly_mmbox), 
491                     mly_mmbox_map_helper, sc, 0);
492     bzero(sc->mly_mmbox, sizeof(*sc->mly_mmbox));
493     return(0);
494
495 }
496
497 /********************************************************************************
498  * Save the physical address of the memory mailbox 
499  */
500 static void
501 mly_mmbox_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
502 {
503     struct mly_softc    *sc = (struct mly_softc *)arg;
504
505     debug_called(2);
506
507     sc->mly_mmbox_busaddr = segs->ds_addr;
508 }
509
510 /********************************************************************************
511  * Free all of the resources associated with (sc)
512  *
513  * Should not be called if the controller is active.
514  */
515 void
516 mly_free(struct mly_softc *sc)
517 {
518     struct mly_command  *mc;
519     
520     debug_called(1);
521
522     /* detach from CAM */
523     mly_cam_detach(sc);
524
525     /* throw away command buffer DMA maps */
526     while (mly_alloc_command(sc, &mc) == 0)
527         bus_dmamap_destroy(sc->mly_buffer_dmat, mc->mc_datamap);
528
529     /* release the packet storage */
530     if (sc->mly_packet != NULL) {
531         bus_dmamap_unload(sc->mly_packet_dmat, sc->mly_packetmap);
532         bus_dmamem_free(sc->mly_packet_dmat, sc->mly_packet, sc->mly_packetmap);
533     }
534
535     /* throw away the controllerinfo structure */
536     if (sc->mly_controllerinfo != NULL)
537         free(sc->mly_controllerinfo, M_DEVBUF);
538
539     /* throw away the controllerparam structure */
540     if (sc->mly_controllerparam != NULL)
541         free(sc->mly_controllerparam, M_DEVBUF);
542
543     /* destroy data-transfer DMA tag */
544     if (sc->mly_buffer_dmat)
545         bus_dma_tag_destroy(sc->mly_buffer_dmat);
546
547     /* free and destroy DMA memory and tag for s/g lists */
548     if (sc->mly_sg_table) {
549         bus_dmamap_unload(sc->mly_sg_dmat, sc->mly_sg_dmamap);
550         bus_dmamem_free(sc->mly_sg_dmat, sc->mly_sg_table, sc->mly_sg_dmamap);
551     }
552     if (sc->mly_sg_dmat)
553         bus_dma_tag_destroy(sc->mly_sg_dmat);
554
555     /* free and destroy DMA memory and tag for memory mailbox */
556     if (sc->mly_mmbox) {
557         bus_dmamap_unload(sc->mly_mmbox_dmat, sc->mly_mmbox_dmamap);
558         bus_dmamem_free(sc->mly_mmbox_dmat, sc->mly_mmbox, sc->mly_mmbox_dmamap);
559     }
560     if (sc->mly_mmbox_dmat)
561         bus_dma_tag_destroy(sc->mly_mmbox_dmat);
562
563     /* disconnect the interrupt handler */
564     if (sc->mly_intr)
565         bus_teardown_intr(sc->mly_dev, sc->mly_irq, sc->mly_intr);
566     if (sc->mly_irq != NULL)
567         bus_release_resource(sc->mly_dev, SYS_RES_IRQ, sc->mly_irq_rid, sc->mly_irq);
568
569     /* destroy the parent DMA tag */
570     if (sc->mly_parent_dmat)
571         bus_dma_tag_destroy(sc->mly_parent_dmat);
572
573     /* release the register window mapping */
574     if (sc->mly_regs_resource != NULL)
575         bus_release_resource(sc->mly_dev, SYS_RES_MEMORY, sc->mly_regs_rid, sc->mly_regs_resource);
576 }
577