diff options
-rw-r--r-- | share/man/man4/Makefile | 4 | ||||
-rw-r--r-- | share/man/man4/aac.4 | 56 | ||||
-rw-r--r-- | share/man/man4/pci.4 | 14 | ||||
-rw-r--r-- | sys/arch/i386/conf/GENERIC | 4 | ||||
-rw-r--r-- | sys/dev/ic/aac.c | 1937 | ||||
-rw-r--r-- | sys/dev/ic/aac_tables.h | 121 | ||||
-rw-r--r-- | sys/dev/ic/aacreg.h | 717 | ||||
-rw-r--r-- | sys/dev/ic/aacvar.h | 341 | ||||
-rw-r--r-- | sys/dev/pci/aac_pci.c | 203 | ||||
-rw-r--r-- | sys/dev/pci/files.pci | 8 |
10 files changed, 3397 insertions, 8 deletions
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 1399052d485..07746dba414 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -1,6 +1,6 @@ -# $OpenBSD: Makefile,v 1.133 2000/11/06 01:41:23 deraadt Exp $ +# $OpenBSD: Makefile,v 1.134 2000/11/10 09:39:37 niklas Exp $ -MAN= ac97.4 adv.4 aha.4 ahb.4 ahc.4 aic.4 amphy.4 an.4 aria.4 ast.4 \ +MAN= aac.4 ac97.4 adv.4 aha.4 ahb.4 ahc.4 aic.4 amphy.4 an.4 aria.4 ast.4 \ atalk.4 atapiscsi.4 audio.4 aue.4 auvia.4 awi.4 bpf.4 bridge.4 \ brgphy.4 cardbus.4 ccd.4 cd.4 ch.4 clnp.4 clcs.4 cltp.4 cmpci.4 cnw.4 \ com.4 cue.4 cy.4 dc.4 ddb.4 de.4 dpt.4 drum.4 eap.4 ec.4 ef.4 \ diff --git a/share/man/man4/aac.4 b/share/man/man4/aac.4 new file mode 100644 index 00000000000..08bc26d8211 --- /dev/null +++ b/share/man/man4/aac.4 @@ -0,0 +1,56 @@ +.\" $OpenBSD: aac.4,v 1.1 2000/11/10 09:39:37 niklas Exp $ +.\" +.\" Copyright (c) 2000 Michael Shalayeff. All rights reserved. +.\" +.\" +.Dd November 7, 2000 +.Dt AAC 4 +.Os +.Sh NAME +.Nm aac +.Nd Adaptec RAID driver +.Sh SYNOPSIS +.Cd "aac* at pci? dev ? function ?" +.Cd "scsibus* at aac?" +.Sh DESCRIPTION +The +.Nm +driver provides support for Adaptec's "FSA" family of raid controllers, +including the +.Tn AAC2622 , +.Tn AAC364 , +.Tn AAC3642 , +.Tn Dell PERC 2/Si , +.Tn Dell PERC 3/Di , +.Tn Dell PERC 3/Si , +.Tn Dell PERC 2/QC , +.Tn Dell PERC 3/QC and +.Tn HP NetRaid-4M +models. +.Pp +All the RAID set volume management is done via the card BIOS. +.Pp +This driver makes drives and/or RAID sets appear as +.Xr sd 4 . +.Sh SEE ALSO +.Xr intro 4 , +.Xr scsi 4 , +.Xr sd 4 +.Sh AUTHOR +The +.Nm +driver was written by +.An Niklas Hallqvist Aq niklas@opensbd.org , +inspired by the FreeBSD driver by +.An Mike Smith Aq msmith@freebsd.org . +.Sh HISTORY +The +.Nm +driver first appeared in +.Ox 2.9 . +.Sh BUGS +.Pp +The card provides a mechanism to do online RAID set management, +unfortunately the protocol is not public. +.Pp +Only Dell PERC 3/Si was tested, but others should work. diff --git a/share/man/man4/pci.4 b/share/man/man4/pci.4 index 9aab163de4f..21ed078f72a 100644 --- a/share/man/man4/pci.4 +++ b/share/man/man4/pci.4 @@ -1,4 +1,4 @@ -.\" $OpenBSD: pci.4,v 1.19 2000/11/08 20:27:08 deraadt Exp $ +.\" $OpenBSD: pci.4,v 1.20 2000/11/10 09:39:37 niklas Exp $ .\" $NetBSD: pci.4,v 1.29 2000/04/01 00:32:23 tsarna Exp $ .\" .\" Copyright (c) 2000 Theo de Raadt. All rights reserved. @@ -105,16 +105,22 @@ interfaces. .El .Ss RAID and cache controllers .Bl -tag -width speaker -offset ind +.It Xr aac 4 +Adaptec "FSA" family (Adaptec AAC, Dell PERC, HP NetRaid) +.Tn RAID +controllers. .It Xr dpt 4 -DPT SmartCache/SmartRAID III and IV SCSI interfaces. +DPT SmartCache/SmartRAID III and IV +.Tn SCSI +controllers. .It Xr gdt 4 ICP-Vortex GDT .Tn RAID -interfaces. +conttrollers. .It Xr twe 4 3ware Escalade .Tn RAID -interfaces. +controllers. .El .Ss Network interfaces .Bl -tag -width speaker -offset ind diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC index fbc126ce726..d85e11d84f1 100644 --- a/sys/arch/i386/conf/GENERIC +++ b/sys/arch/i386/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.212 2000/10/26 20:50:44 aaron Exp $ +# $OpenBSD: GENERIC,v 1.213 2000/11/10 09:39:37 niklas Exp $ # $NetBSD: GENERIC,v 1.48 1996/05/20 18:17:23 mrg Exp $ # # GENERIC -- everything that's currently supported @@ -188,6 +188,8 @@ gdt* at pci? dev ? function ? # ICP Vortex GDT RAID controllers scsibus* at gdt? twe* at pci? dev ? function ? # 3ware Escalade RAID controllers scsibus* at twe? +aac* at pci? dev ? function ? # Adaptec FSA RAID controllers +scsibus* at aac? isp* at pci? dev ? function ? # Qlogic ISP [12]0x0 SCSI/FibreChannel scsibus* at isp? aic0 at isa? port 0x340 irq 11 # Adaptec 152[02] SCSI controllers diff --git a/sys/dev/ic/aac.c b/sys/dev/ic/aac.c new file mode 100644 index 00000000000..87b31467d8d --- /dev/null +++ b/sys/dev/ic/aac.c @@ -0,0 +1,1937 @@ +/* $OpenBSD: aac.c,v 1.1 2000/11/10 09:39:35 niklas Exp $ */ + +/*- + * Copyright (c) 2000 Michael Smith + * Copyright (c) 2000 BSDi + * Copyright (c) 2000 Niklas Hallqvist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: /c/ncvs/src/sys/dev/aac/aac.c,v 1.1 2000/09/13 03:20:34 msmith Exp $ + */ + +/* + * Driver for the Adaptec 'FSA' family of PCI/SCSI RAID adapters. + */ + +/* + * This driver would not have rewritten for OpenBSD if it was not for the + * hardware donation from Nocom. I want to thank them for their support. + * Of course, credit should go to Mike Smith for the original work he did + * in the FreeBSD driver where I found lots of reusable code and inspiration. + * - Niklas Hallqvist + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/malloc.h> + +#include <machine/bus.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsi_disk.h> +#include <scsi/scsiconf.h> + +#include <dev/ic/aacreg.h> +#include <dev/ic/aacvar.h> +#include <dev/ic/aac_tables.h> + +/* XXX from stddef.h */ +#define offsetof(type, member) ((size_t)(&((type *)0)->member)) + +/* Geometry constants. */ +#define AAC_MAXCYLS 1024 +#define AAC_HEADS 64 +#define AAC_SECS 32 /* mapping 64*32 */ +#define AAC_MEDHEADS 127 +#define AAC_MEDSECS 63 /* mapping 127*63 */ +#define AAC_BIGHEADS 255 +#define AAC_BIGSECS 63 /* mapping 255*63 */ +#define AAC_SECS32 0x1f /* round capacity */ + +void aac_bio_complete __P((struct aac_ccb *)); +void aac_complete __P((void *, int)); +void aac_copy_internal_data __P((struct scsi_xfer *, u_int8_t *, size_t)); +struct scsi_xfer *aac_dequeue __P((struct aac_softc *)); +int aac_dequeue_fib __P((struct aac_softc *, int, u_int32_t *, + struct aac_fib **)); +char *aac_describe_code __P((struct aac_code_lookup *, u_int32_t)); +void aac_describe_controller __P((struct aac_softc *)); +void aac_enqueue __P((struct aac_softc *, struct scsi_xfer *, int)); +void aac_enqueue_ccb __P((struct aac_softc *, struct aac_ccb *)); +int aac_enqueue_fib __P((struct aac_softc *, int, u_int32_t, u_int32_t)); +void aac_eval_mapping __P((u_int32_t, int *, int *, int *)); +int aac_exec_ccb __P((struct aac_ccb *)); +void aac_free_ccb __P((struct aac_softc *, struct aac_ccb *)); +struct aac_ccb *aac_get_ccb __P((struct aac_softc *, int)); +#if 0 +void aac_handle_aif __P((struct aac_softc *, struct aac_aif_command *)); +#endif +void aac_host_command __P((struct aac_softc *)); +void aac_host_response __P((struct aac_softc *)); +int aac_init __P((struct aac_softc *)); +int aac_internal_cache_cmd __P((struct scsi_xfer *)); +int aac_map_command __P((struct aac_ccb *)); +#ifdef AAC_DEBUG +void aac_print_fib __P((struct aac_softc *, struct aac_fib *, char *)); +#endif +int aac_raw_scsi_cmd __P((struct scsi_xfer *)); +int aac_scsi_cmd __P((struct scsi_xfer *)); +int aac_start __P((struct aac_ccb *)); +void aac_start_ccbs __P((struct aac_softc *)); +void aac_startup __P((struct aac_softc *)); +int aac_sync_command __P((struct aac_softc *, u_int32_t, u_int32_t, + u_int32_t, u_int32_t, u_int32_t, u_int32_t *)); +int aac_sync_fib __P((struct aac_softc *, u_int32_t, u_int32_t, void *, + u_int16_t, void *, u_int16_t *)); +void aac_timeout __P((void *)); +void aac_unmap_command __P((struct aac_ccb *)); +void aac_watchdog __P((void *)); + +struct cfdriver aac_cd = { + NULL, "aac", DV_DULL +}; + +struct scsi_adapter aac_switch = { + aac_scsi_cmd, aacminphys, 0, 0, +}; + +struct scsi_adapter aac_raw_switch = { + aac_raw_scsi_cmd, aacminphys, 0, 0, +}; + +struct scsi_device aac_dev = { + NULL, NULL, NULL, NULL +}; + +/* i960Rx interface */ +int aac_rx_get_fwstatus __P((struct aac_softc *)); +void aac_rx_qnotify __P((struct aac_softc *, int)); +int aac_rx_get_istatus __P((struct aac_softc *)); +void aac_rx_clear_istatus __P((struct aac_softc *, int)); +void aac_rx_set_mailbox __P((struct aac_softc *, u_int32_t, u_int32_t, + u_int32_t, u_int32_t, u_int32_t)); +int aac_rx_get_mailboxstatus __P((struct aac_softc *)); +void aac_rx_set_interrupts __P((struct aac_softc *, int)); + +/* StrongARM interface */ +int aac_sa_get_fwstatus __P((struct aac_softc *)); +void aac_sa_qnotify __P((struct aac_softc *, int)); +int aac_sa_get_istatus __P((struct aac_softc *)); +void aac_sa_clear_istatus __P((struct aac_softc *, int)); +void aac_sa_set_mailbox __P((struct aac_softc *, u_int32_t, u_int32_t, + u_int32_t, u_int32_t, u_int32_t)); +int aac_sa_get_mailboxstatus __P((struct aac_softc *)); +void aac_sa_set_interrupts __P((struct aac_softc *, int)); + +struct aac_interface aac_rx_interface = { + aac_rx_get_fwstatus, + aac_rx_qnotify, + aac_rx_get_istatus, + aac_rx_clear_istatus, + aac_rx_set_mailbox, + aac_rx_get_mailboxstatus, + aac_rx_set_interrupts +}; + +struct aac_interface aac_sa_interface = { + aac_sa_get_fwstatus, + aac_sa_qnotify, + aac_sa_get_istatus, + aac_sa_clear_istatus, + aac_sa_set_mailbox, + aac_sa_get_mailboxstatus, + aac_sa_set_interrupts +}; + +#ifdef AAC_DEBUG +int aac_debug = AAC_DEBUG; +#endif + +int +aac_attach(sc) + struct aac_softc *sc; +{ + int i, error; + bus_dma_segment_t seg; + int nsegs; + struct aac_ccb *ccb; + + TAILQ_INIT(&sc->sc_free_ccb); + TAILQ_INIT(&sc->sc_ccbq); + TAILQ_INIT(&sc->sc_completed); + LIST_INIT(&sc->sc_queue); + + /* disable interrupts before we enable anything */ + AAC_MASK_INTERRUPTS(sc); + + /* mark controller as suspended until we get ourselves organised */ + sc->sc_state |= AAC_STATE_SUSPEND; + + /* + * Initialise the adapter. + */ + error = aac_init(sc); + if (error) + return (error); + + /* + * Print a little information about the controller. + */ + aac_describe_controller(sc); + + /* Initialize the ccbs */ + for (i = 0; i < AAC_ADAP_NORM_CMD_ENTRIES; i++) { + ccb = &sc->sc_ccbs[i]; + error = bus_dmamap_create(sc->sc_dmat, + (AAC_MAXSGENTRIES - 1) << PGSHIFT, AAC_MAXSGENTRIES, + (AAC_MAXSGENTRIES - 1) << PGSHIFT, 0, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &ccb->ac_dmamap_xfer); + if (error) { + printf("%s: cannot create ccb dmamap (%d)", + sc->sc_dev.dv_xname, error); + /* XXX cleanup */ + return (1); + } + + /* allocate the FIB cluster in DMAable memory and load it */ + if (bus_dmamem_alloc(sc->sc_dmat, sizeof *ccb->ac_fib, 1, 0, + &seg, 1, &nsegs, BUS_DMA_NOWAIT)) { + printf("%s: can't allocate FIB structure\n", + sc->sc_dev.dv_xname); + /* XXX cleanup */ + return (1); + } + ccb->ac_fibphys = seg.ds_addr; + if (bus_dmamem_map(sc->sc_dmat, &seg, nsegs, + sizeof *ccb->ac_fib, (caddr_t *)&ccb->ac_fib, 0)) { + printf("%s: can't map FIB structure\n", + sc->sc_dev.dv_xname); + /* XXX cleanup */ + return (1); + } + + TAILQ_INSERT_TAIL(&sc->sc_free_ccb, &sc->sc_ccbs[i], + ac_chain); + } + + /* Fill in the prototype scsi_link. */ + sc->sc_link.adapter_softc = sc; + sc->sc_link.adapter = &aac_switch; + sc->sc_link.device = &aac_dev; + sc->sc_link.openings = AAC_ADAP_NORM_CMD_ENTRIES; /* XXX optimal? */ + sc->sc_link.adapter_buswidth = AAC_MAX_CONTAINERS; + sc->sc_link.adapter_target = AAC_MAX_CONTAINERS; + + config_found(&sc->sc_dev, &sc->sc_link, scsiprint); + + return (0); +} + +/* + * Look up a text description of a numeric error code and return a pointer to + * same. + */ +char * +aac_describe_code(table, code) + struct aac_code_lookup *table; + u_int32_t code; +{ + int i; + + for (i = 0; table[i].string != NULL; i++) + if (table[i].code == code) + return (table[i].string); + return (table[i + 1].string); +} + +void +aac_describe_controller(sc) + struct aac_softc *sc; +{ + u_int8_t buf[AAC_FIB_DATASIZE]; /* XXX a bit big for the stack */ + u_int16_t bufsize; + struct aac_adapter_info *info; + u_int8_t arg; + + arg = 0; + if (aac_sync_fib(sc, RequestAdapterInfo, 0, &arg, sizeof arg, &buf, + &bufsize)) { + printf("%s: RequestAdapterInfo failed\n", sc->sc_dev.dv_xname); + return; + } + if (bufsize != sizeof *info) { + printf("%s: " + "RequestAdapterInfo returned wrong data size (%d != %d)\n", + sc->sc_dev.dv_xname, bufsize, sizeof *info); + return; + } + info = (struct aac_adapter_info *)&buf[0]; + + printf("%s: %s %dMHz, %dMB, %s (%d) Kernel %d.%d-%d\n", + sc->sc_dev.dv_xname, + aac_describe_code(aac_cpu_variant, info->CpuVariant), + info->ClockSpeed, info->TotalMem / (1024 * 1024), + aac_describe_code(aac_battery_platform, info->batteryPlatform), + info->batteryPlatform, info->KernelRevision.external.comp.major, + info->KernelRevision.external.comp.minor, + info->KernelRevision.external.comp.dash); + + /* save the kernel revision structure for later use */ + sc->sc_revision = info->KernelRevision; +} + +int +aac_init(sc) + struct aac_softc *sc; +{ + bus_dma_segment_t seg; + int nsegs; + int i, error; + int state = 0; + struct aac_adapter_init *ip; + u_int32_t code; + u_int8_t *qaddr; + + /* + * First wait for the adapter to come ready. + */ + for (i = 0; i < AAC_BOOT_TIMEOUT * 1000; i++) { + code = AAC_GET_FWSTATUS(sc); + if (code & AAC_SELF_TEST_FAILED) { + printf("%s: FATAL: selftest failed\n", + sc->sc_dev.dv_xname); + return (ENXIO); + } + if (code & AAC_KERNEL_PANIC) { + printf("%s: FATAL: controller kernel panic\n", + sc->sc_dev.dv_xname); + return (ENXIO); + } + if (code & AAC_UP_AND_RUNNING) + break; + DELAY(1000); + } + if (i == AAC_BOOT_TIMEOUT * 1000) { + printf("%s: FATAL: controller not coming ready, status %x\n", + sc->sc_dev.dv_xname, code); + return (ENXIO); + } + + if (bus_dmamem_alloc(sc->sc_dmat, sizeof *sc->sc_common, 1, 0, &seg, 1, + &nsegs, BUS_DMA_NOWAIT)) { + printf("%s: can't allocate common structure\n", + sc->sc_dev.dv_xname); + return (ENOMEM); + } + state++; + sc->sc_common_busaddr = seg.ds_addr; + if (bus_dmamem_map(sc->sc_dmat, &seg, nsegs, sizeof *sc->sc_common, + (caddr_t *)&sc->sc_common, 0)) { + printf("%s: can't map common structure\n", + sc->sc_dev.dv_xname); + error = ENOMEM; + goto bail_out; + } + state++; + bzero(sc->sc_common, sizeof *sc->sc_common); + + /* + * Fill in the init structure. This tells the adapter about + * the physical location * of various important shared data + * structures. + */ + ip = &sc->sc_common->ac_init; + ip->InitStructRevision = AAC_INIT_STRUCT_REVISION; + + ip->AdapterFibsPhysicalAddress = + sc->sc_common_busaddr + offsetof(struct aac_common, ac_fibs); + ip->AdapterFibsVirtualAddress = &sc->sc_common->ac_fibs[0]; + ip->AdapterFibsSize = AAC_ADAPTER_FIBS * sizeof(struct aac_fib); + ip->AdapterFibAlign = sizeof(struct aac_fib); + + ip->PrintfBufferAddress = + sc->sc_common_busaddr + offsetof(struct aac_common, ac_printf); + ip->PrintfBufferSize = AAC_PRINTF_BUFSIZE; + + ip->HostPhysMemPages = 0; /* not used? */ + ip->HostElapsedSeconds = 0; /* reset later if invalid */ + + /* + * Initialise FIB queues. Note that it appears that the + * layout of the indexes and the segmentation of the entries + * is mandated by the adapter, which is only told about the + * base of the queue index fields. + * + * The initial values of the indices are assumed to inform the + * adapter of the sizes of the respective queues. + * + * The Linux driver uses a much more complex scheme whereby + * several header * records are kept for each queue. We use a + * couple of generic list manipulation functions which + * 'know' the size of each list by virtue of a table. + */ + qaddr = &sc->sc_common->ac_qbuf[0] + AAC_QUEUE_ALIGN; + qaddr -= (u_int32_t)qaddr % AAC_QUEUE_ALIGN; /* XXX not portable */ + sc->sc_queues = (struct aac_queue_table *)qaddr; + ip->CommHeaderAddress = sc->sc_common_busaddr + + ((char *)sc->sc_queues - (char *)sc->sc_common); + bzero(sc->sc_queues, sizeof(struct aac_queue_table)); + + sc->sc_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] = + AAC_HOST_NORM_CMD_ENTRIES; + sc->sc_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] = + AAC_HOST_NORM_CMD_ENTRIES; + sc->sc_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] = + AAC_HOST_HIGH_CMD_ENTRIES; + sc->sc_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] = + AAC_HOST_HIGH_CMD_ENTRIES; + sc->sc_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] = + AAC_ADAP_NORM_CMD_ENTRIES; + sc->sc_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] = + AAC_ADAP_NORM_CMD_ENTRIES; + sc->sc_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] = + AAC_ADAP_HIGH_CMD_ENTRIES; + sc->sc_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] = + AAC_ADAP_HIGH_CMD_ENTRIES; + sc->sc_queues-> + qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX] = + AAC_HOST_NORM_RESP_ENTRIES; + sc->sc_queues-> + qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX] = + AAC_HOST_NORM_RESP_ENTRIES; + sc->sc_queues-> + qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX] = + AAC_HOST_HIGH_RESP_ENTRIES; + sc->sc_queues-> + qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX] = + AAC_HOST_HIGH_RESP_ENTRIES; + sc->sc_queues-> + qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX] = + AAC_ADAP_NORM_RESP_ENTRIES; + sc->sc_queues-> + qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX] = + AAC_ADAP_NORM_RESP_ENTRIES; + sc->sc_queues-> + qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX] = + AAC_ADAP_HIGH_RESP_ENTRIES; + sc->sc_queues-> + qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX] = + AAC_ADAP_HIGH_RESP_ENTRIES; + sc->sc_qentries[AAC_HOST_NORM_CMD_QUEUE] = + &sc->sc_queues->qt_HostNormCmdQueue[0]; + sc->sc_qentries[AAC_HOST_HIGH_CMD_QUEUE] = + &sc->sc_queues->qt_HostHighCmdQueue[0]; + sc->sc_qentries[AAC_ADAP_NORM_CMD_QUEUE] = + &sc->sc_queues->qt_AdapNormCmdQueue[0]; + sc->sc_qentries[AAC_ADAP_HIGH_CMD_QUEUE] = + &sc->sc_queues->qt_AdapHighCmdQueue[0]; + sc->sc_qentries[AAC_HOST_NORM_RESP_QUEUE] = + &sc->sc_queues->qt_HostNormRespQueue[0]; + sc->sc_qentries[AAC_HOST_HIGH_RESP_QUEUE] = + &sc->sc_queues->qt_HostHighRespQueue[0]; + sc->sc_qentries[AAC_ADAP_NORM_RESP_QUEUE] = + &sc->sc_queues->qt_AdapNormRespQueue[0]; + sc->sc_qentries[AAC_ADAP_HIGH_RESP_QUEUE] = + &sc->sc_queues->qt_AdapHighRespQueue[0]; + + /* + * Do controller-type-specific initialisation + */ + switch (sc->sc_hwif) { + case AAC_HWIF_I960RX: + AAC_SETREG4(sc, AAC_RX_ODBR, ~0); + break; + } + + /* + * Give the init structure to the controller. + */ + if (aac_sync_command(sc, AAC_MONKER_INITSTRUCT, + sc->sc_common_busaddr + offsetof(struct aac_common, ac_init), 0, 0, + 0, NULL)) { + printf("%s: error establishing init structure\n", + sc->sc_dev.dv_xname); + error = EIO; + goto bail_out; + } + + aac_startup(sc); + + return (0); + + bail_out: + if (state > 1) + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_common, + sizeof *sc->sc_common); + if (state > 0) + bus_dmamem_free(sc->sc_dmat, &seg, 1); + return (error); +} + +/* + * Probe for containers, create disks. + */ +void +aac_startup (sc) + struct aac_softc *sc; +{ + struct aac_mntinfo mi; + struct aac_mntinforesponse mir; + u_int16_t rsize; + int i, drv_cyls, drv_hds, drv_secs; + + /* loop over possible containers */ + mi.Command = VM_NameServe; + mi.MntType = FT_FILESYS; + for (i = 0; i < AAC_MAX_CONTAINERS; i++) { + /* request information on this container */ + mi.MntCount = i; + if (aac_sync_fib(sc, ContainerCommand, 0, &mi, sizeof mi, &mir, + &rsize)) { + printf("%s: error probing container %d", + sc->sc_dev.dv_xname, i); + continue; + } + /* check response size */ + if (rsize != sizeof mir) { + printf("%s: container info response wrong size " + "(%d should be %d)", + sc->sc_dev.dv_xname, rsize, sizeof mir); + continue; + } + + /* + * Check container volume type for validity. Note + * that many of the possible types * may never show + * up. + */ + if (mir.Status == ST_OK && + mir.MntTable[0].VolType != CT_NONE) { + AAC_DPRINTF(AAC_D_MISC, + ("%d: id %x name '%.16s' size %u type %d", i, + mir.MntTable[0].ObjectId, + mir.MntTable[0].FileSystemName, + mir.MntTable[0].Capacity, + mir.MntTable[0].VolType)); + + sc->sc_hdr[i].hd_present = 1; + sc->sc_hdr[i].hd_size = mir.MntTable[0].Capacity; + + /* + * Evaluate mapping (sectors per head, heads per cyl) + */ + sc->sc_hdr[i].hd_size &= ~AAC_SECS32; + aac_eval_mapping(sc->sc_hdr[i].hd_size, &drv_cyls, + &drv_hds, &drv_secs); + sc->sc_hdr[i].hd_heads = drv_hds; + sc->sc_hdr[i].hd_secs = drv_secs; + /* Round the size */ + sc->sc_hdr[i].hd_size = drv_cyls * drv_hds * drv_secs; + + sc->sc_hdr[i].hd_devtype = mir.MntTable[0].VolType; + + /* XXX Save the name too for use in IDENTIFY later */ + } + } + + /* mark the controller up */ + sc->sc_state &= ~AAC_STATE_SUSPEND; + + /* enable interrupts now */ + AAC_UNMASK_INTERRUPTS(sc); +} + +void +aac_eval_mapping(size, cyls, heads, secs) + u_int32_t size; + int *cyls, *heads, *secs; +{ + *cyls = size / AAC_HEADS / AAC_SECS; + if (*cyls < AAC_MAXCYLS) { + *heads = AAC_HEADS; + *secs = AAC_SECS; + } else { + /* Too high for 64 * 32 */ + *cyls = size / AAC_MEDHEADS / AAC_MEDSECS; + if (*cyls < AAC_MAXCYLS) { + *heads = AAC_MEDHEADS; + *secs = AAC_MEDSECS; + } else { + /* Too high for 127 * 63 */ + *cyls = size / AAC_BIGHEADS / AAC_BIGSECS; + *heads = AAC_BIGHEADS; + *secs = AAC_BIGSECS; + } + } +} + +int +aac_raw_scsi_cmd(xs) + struct scsi_xfer *xs; +{ + AAC_DPRINTF(AAC_D_CMD, ("aac_raw_scsi_cmd ")); + + /* XXX Not yet implemented */ + xs->error = XS_DRIVER_STUFFUP; + return (COMPLETE); +} + +int +aac_scsi_cmd(xs) + struct scsi_xfer *xs; +{ + struct scsi_link *link = xs->sc_link; + struct aac_softc *sc = link->adapter_softc; + u_int8_t target = link->target; + struct aac_ccb *ccb; + u_int32_t blockno, blockcnt; + struct scsi_rw *rw; + struct scsi_rw_big *rwb; + aac_lock_t lock; + int retval = SUCCESSFULLY_QUEUED; + + AAC_DPRINTF(AAC_D_CMD, ("aac_scsi_cmd ")); + + xs->error = XS_NOERROR; + + if (target >= AAC_MAX_CONTAINERS || !sc->sc_hdr[target].hd_present || + link->lun != 0) { + /* + * XXX Should be XS_SENSE but that would require setting up a + * faked sense too. + */ + xs->error = XS_DRIVER_STUFFUP; + xs->flags |= ITSDONE; + scsi_done(xs); + return (COMPLETE); + } + + lock = AAC_LOCK(sc); + + /* Don't double enqueue if we came from gdt_chain. */ + if (xs != LIST_FIRST(&sc->sc_queue)) + aac_enqueue(sc, xs, 0); + + while ((xs = aac_dequeue(sc))) { + xs->error = XS_NOERROR; + ccb = NULL; + + switch (xs->cmd->opcode) { + case TEST_UNIT_READY: + case REQUEST_SENSE: + case INQUIRY: + case MODE_SENSE: + case START_STOP: + case READ_CAPACITY: +#if 0 + case VERIFY: +#endif + if (!aac_internal_cache_cmd(xs)) { + AAC_UNLOCK(sc, lock); + return (TRY_AGAIN_LATER); + } + xs->flags |= ITSDONE; + scsi_done(xs); + goto ready; + + case PREVENT_ALLOW: + AAC_DPRINTF(AAC_D_CMD, ("PREVENT/ALLOW ")); + /* XXX Not yet implemented */ + xs->error = XS_NOERROR; + xs->flags |= ITSDONE; + scsi_done(xs); + goto ready; + + case SYNCHRONIZE_CACHE: + AAC_DPRINTF(AAC_D_CMD, ("SYNCHRONIZE_CACHE ")); + /* XXX Not yet implemented */ + xs->error = XS_NOERROR; + xs->flags |= ITSDONE; + scsi_done(xs); + goto ready; + + default: + AAC_DPRINTF(AAC_D_CMD, + ("unknown opc %d ", xs->cmd->opcode)); + /* XXX Not yet implemented */ + xs->error = XS_DRIVER_STUFFUP; + xs->flags |= ITSDONE; + scsi_done(xs); + goto ready; + + case READ_COMMAND: + case READ_BIG: + case WRITE_COMMAND: + case WRITE_BIG: + AAC_DPRINTF(AAC_D_CMD, + ("rw opc %d ", xs->cmd->opcode)); + + if (xs->cmd->opcode != SYNCHRONIZE_CACHE) { + /* A read or write operation. */ + if (xs->cmdlen == 6) { + rw = (struct scsi_rw *)xs->cmd; + blockno = _3btol(rw->addr) & + (SRW_TOPADDR << 16 | 0xffff); + blockcnt = + rw->length ? rw->length : 0x100; + } else { + rwb = (struct scsi_rw_big *)xs->cmd; + blockno = _4btol(rwb->addr); + blockcnt = _2btol(rwb->length); + } + if (blockno >= sc->sc_hdr[target].hd_size || + blockno + blockcnt > + sc->sc_hdr[target].hd_size) { + printf( + "%s: out of bounds %u-%u >= %u\n", + sc->sc_dev.dv_xname, blockno, + blockcnt, + sc->sc_hdr[target].hd_size); + /* + * XXX Should be XS_SENSE but that + * would require setting up a faked + * sense too. + */ + xs->error = XS_DRIVER_STUFFUP; + xs->flags |= ITSDONE; + scsi_done(xs); + goto ready; + } + } + + ccb = aac_get_ccb(sc, xs->flags); + + /* + * Are we out of commands, something is wrong. + * + */ + if (ccb == NULL) { + printf("%s: no ccb in aac_scsi_cmd", + sc->sc_dev.dv_xname); + xs->error = XS_DRIVER_STUFFUP; + xs->flags |= ITSDONE; + scsi_done(xs); + goto ready; + } + + ccb->ac_blockno = blockno; + ccb->ac_blockcnt = blockcnt; + ccb->ac_xs = xs; + ccb->ac_timeout = xs->timeout; + + if (xs->cmd->opcode != SYNCHRONIZE_CACHE && + aac_map_command(ccb)) { + aac_free_ccb(sc, ccb); + xs->error = XS_DRIVER_STUFFUP; + xs->flags |= ITSDONE; + scsi_done(xs); + goto ready; + } + + aac_enqueue_ccb(sc, ccb); + /* XXX what if enqueue did not start a transfer? */ + if (xs->flags & SCSI_POLL) { +#if 0 + if (!aac_wait(sc, ccb, ccb->ac_timeout)) { + AAC_UNLOCK(sc, lock); + printf("%s: command timed out\n", + sc->sc_dev.dv_xname); + xs->error = XS_TIMEOUT; + return (TRY_AGAIN_LATER); + } + xs->flags |= ITSDONE; + scsi_done(xs); +#endif + } + } + + ready: + /* + * Don't process the queue if we are polling. + */ + if (xs->flags & SCSI_POLL) { + retval = COMPLETE; + break; + } + } + + AAC_UNLOCK(sc, lock); + return (retval); +} + +void +aac_copy_internal_data(xs, data, size) + struct scsi_xfer *xs; + u_int8_t *data; + size_t size; +{ + size_t copy_cnt; + + AAC_DPRINTF(AAC_D_MISC, ("aac_copy_internal_data ")); + + if (!xs->datalen) + printf("uio move not yet supported\n"); + else { + copy_cnt = MIN(size, xs->datalen); + bcopy(data, xs->data, copy_cnt); + } +} + +/* Emulated SCSI operation on cache device */ +int +aac_internal_cache_cmd(xs) + struct scsi_xfer *xs; +{ + struct scsi_link *link = xs->sc_link; + struct aac_softc *sc = link->adapter_softc; + struct scsi_inquiry_data inq; + struct scsi_sense_data sd; + struct { + struct scsi_mode_header hd; + struct scsi_blk_desc bd; + union scsi_disk_pages dp; + } mpd; + struct scsi_read_cap_data rcd; + u_int8_t target = link->target; + + AAC_DPRINTF(AAC_D_CMD, ("aac_internal_cache_cmd ")); + + switch (xs->cmd->opcode) { + case TEST_UNIT_READY: + case START_STOP: +#if 0 + case VERIFY: +#endif + AAC_DPRINTF(AAC_D_CMD, ("opc %d tgt %d ", xs->cmd->opcode, + target)); + break; + + case REQUEST_SENSE: + AAC_DPRINTF(AAC_D_CMD, ("REQUEST SENSE tgt %d ", target)); + bzero(&sd, sizeof sd); + sd.error_code = 0x70; + sd.segment = 0; + sd.flags = SKEY_NO_SENSE; + aac_enc32(sd.info, 0); + sd.extra_len = 0; + aac_copy_internal_data(xs, (u_int8_t *)&sd, sizeof sd); + break; + + case INQUIRY: + AAC_DPRINTF(AAC_D_CMD, ("INQUIRY tgt %d devtype %x ", target, + sc->sc_hdr[target].hd_devtype)); + bzero(&inq, sizeof inq); + /* XXX How do we detect removable/CD-ROM devices? */ + inq.device = T_DIRECT; + inq.dev_qual2 = 0; + inq.version = 2; + inq.response_format = 2; + inq.additional_length = 32; + strcpy(inq.vendor, "Adaptec"); + sprintf(inq.product, "Container #%02d", target); + strcpy(inq.revision, " "); + aac_copy_internal_data(xs, (u_int8_t *)&inq, sizeof inq); + break; + + case MODE_SENSE: + AAC_DPRINTF(AAC_D_CMD, ("MODE SENSE tgt %d ", target)); + + bzero(&mpd, sizeof mpd); + switch (((struct scsi_mode_sense *)xs->cmd)->page) { + case 4: + /* scsi_disk.h says this should be 0x16 */ + mpd.dp.rigid_geometry.pg_length = 0x16; + mpd.hd.data_length = sizeof mpd.hd + sizeof mpd.bd + + mpd.dp.rigid_geometry.pg_length; + mpd.hd.blk_desc_len = sizeof mpd.bd; + + /* XXX */ + mpd.hd.dev_spec = 0; + _lto3b(AAC_BLOCK_SIZE, mpd.bd.blklen); + mpd.dp.rigid_geometry.pg_code = 4; + _lto3b(sc->sc_hdr[target].hd_size / + sc->sc_hdr[target].hd_heads / + sc->sc_hdr[target].hd_secs, + mpd.dp.rigid_geometry.ncyl); + mpd.dp.rigid_geometry.nheads = + sc->sc_hdr[target].hd_heads; + aac_copy_internal_data(xs, (u_int8_t *)&mpd, + sizeof mpd); + break; + + default: + printf("%s: mode sense page %d not simulated\n", + sc->sc_dev.dv_xname, + ((struct scsi_mode_sense *)xs->cmd)->page); + xs->error = XS_DRIVER_STUFFUP; + return (0); + } + break; + + case READ_CAPACITY: + AAC_DPRINTF(AAC_D_CMD, ("READ CAPACITY tgt %d ", target)); + bzero(&rcd, sizeof rcd); + _lto4b(sc->sc_hdr[target].hd_size - 1, rcd.addr); + _lto4b(AAC_BLOCK_SIZE, rcd.length); + aac_copy_internal_data(xs, (u_int8_t *)&rcd, sizeof rcd); + break; + + default: + printf("aac_internal_cache_cmd got bad opcode: %d\n", + xs->cmd->opcode); + xs->error = XS_DRIVER_STUFFUP; + return (0); + } + + xs->error = XS_NOERROR; + return (1); +} + +/* + * Take an interrupt. + */ +int +aac_intr(arg) + void *arg; +{ + struct aac_softc *sc = arg; + u_int16_t reason; + int claimed = 0; + + AAC_DPRINTF(AAC_D_INTR, ("aac_intr(%p) ", sc)); + + reason = AAC_GET_ISTATUS(sc); + AAC_DPRINTF(AAC_D_INTR, ("istatus 0x%04x ", reason)); + + /* controller wants to talk to the log? XXX should we defer this? */ + if (reason & AAC_DB_PRINTF) { + if (sc->sc_common->ac_printf[0]) { + printf("%s: ** %.*s", sc->sc_dev.dv_xname, + AAC_PRINTF_BUFSIZE, sc->sc_common->ac_printf); + sc->sc_common->ac_printf[0] = 0; + } + AAC_CLEAR_ISTATUS(sc, AAC_DB_PRINTF); + AAC_QNOTIFY(sc, AAC_DB_PRINTF); + claimed = 1; + } + + /* Controller has a message for us? */ + if (reason & AAC_DB_COMMAND_READY) { + aac_host_command(sc); + AAC_CLEAR_ISTATUS(sc, AAC_DB_COMMAND_READY); + claimed = 1; + } + + /* Controller has a response for us? */ + if (reason & AAC_DB_RESPONSE_READY) { + aac_host_response(sc); + AAC_CLEAR_ISTATUS(sc, AAC_DB_RESPONSE_READY); + claimed = 1; + } + + /* + * Spurious interrupts that we don't use - reset the mask and clear + * the interrupts. + */ + if (reason & (AAC_DB_SYNC_COMMAND | AAC_DB_COMMAND_NOT_FULL | + AAC_DB_RESPONSE_NOT_FULL)) { + AAC_UNMASK_INTERRUPTS(sc); + AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND | + AAC_DB_COMMAND_NOT_FULL | AAC_DB_RESPONSE_NOT_FULL); + claimed = 1; + } + + return (claimed); +} + +/* + * Handle notification of one or more FIBs coming from the controller. + */ +void +aac_host_command(struct aac_softc *sc) +{ + struct aac_fib *fib; + u_int32_t fib_size; + + for (;;) { + if (aac_dequeue_fib(sc, AAC_HOST_NORM_CMD_QUEUE, &fib_size, + &fib)) + break; /* nothing to do */ + + switch(fib->Header.Command) { + case AifRequest: +#if 0 + aac_handle_aif(sc, + (struct aac_aif_command *)&fib->data[0]); +#endif + + break; + default: + printf("%s: unknown command from controller\n", + sc->sc_dev.dv_xname); + AAC_PRINT_FIB(sc, fib); + break; + } + + /* XXX reply to FIBs requesting responses ?? */ + /* XXX how do we return these FIBs to the controller? */ + } +} + +/* + * Handle notification of one or more FIBs completed by the controller + */ +void +aac_host_response(struct aac_softc *sc) +{ + struct aac_ccb *ccb; + struct aac_fib *fib; + u_int32_t fib_size; + + for (;;) { + /* look for completed FIBs on our queue */ + if (aac_dequeue_fib(sc, AAC_HOST_NORM_RESP_QUEUE, &fib_size, + &fib)) + break; /* nothing to do */ + + /* get the command, unmap and queue for later processing */ + ccb = (struct aac_ccb *)fib->Header.SenderData; + if (ccb == NULL) { + AAC_PRINT_FIB(sc, fib); + } else { + untimeout(aac_timeout, ccb); + aac_unmap_command(ccb); /* XXX defer? */ + aac_enqueue_completed(ccb); + } + } + + /* handle completion processing */ + aac_complete(sc, 0); +} + +/* + * Process completed commands. + */ +void +aac_complete(void *context, int pending) +{ + struct aac_softc *sc = (struct aac_softc *)context; + struct aac_ccb *ccb; + + /* pull completed commands off the queue */ + for (;;) { + ccb = aac_dequeue_completed(sc); + if (ccb == NULL) + return; + ccb->ac_flags |= AAC_ACF_COMPLETED; + +#if 0 + /* is there a completion handler? */ + if (ccb->ac_complete != NULL) { + ccb->ac_complete(ccb); + } else { + /* assume that someone is sleeping on this command */ + wakeup(ccb); + } +#else + aac_bio_complete(ccb); +#endif + } +} + +/* + * Handle a bio-instigated command that has been completed. + */ +void +aac_bio_complete(struct aac_ccb *ccb) +{ + struct scsi_xfer *xs = ccb->ac_xs; + struct aac_softc *sc = xs->sc_link->adapter_softc; + struct buf *bp = xs->bp; + struct aac_blockread_response *brr; + struct aac_blockwrite_response *bwr; + AAC_FSAStatus status; + + /* fetch relevant status and then release the command */ + if (bp->b_flags & B_READ) { + brr = (struct aac_blockread_response *)&ccb->ac_fib->data[0]; + status = brr->Status; + } else { + bwr = (struct aac_blockwrite_response *)&ccb->ac_fib->data[0]; + status = bwr->Status; + } + aac_free_ccb(sc, ccb); + + /* fix up the bio based on status */ + if (status == ST_OK) { + bp->b_resid = 0; + } else { + bp->b_error = EIO; + bp->b_flags |= B_ERROR; + + /* XXX be more verbose? */ + printf("%s: I/O error %d (%s)\n", status, + AAC_COMMAND_STATUS(status)); + } + scsi_done(xs); +} + +/* + * Send a synchronous command to the controller and wait for a result. + */ +int +aac_sync_command(sc, command, arg0, arg1, arg2, arg3, sp) + struct aac_softc *sc; + u_int32_t command; + u_int32_t arg0; + u_int32_t arg1; + u_int32_t arg2; + u_int32_t arg3; + u_int32_t *sp; +{ + int i; + u_int32_t status; + aac_lock_t lock = AAC_LOCK(sc); + + /* populate the mailbox */ + AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3); + + /* ensure the sync command doorbell flag is cleared */ + AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND); + + /* then set it to signal the adapter */ + AAC_QNOTIFY(sc, AAC_DB_SYNC_COMMAND); + DELAY(AAC_SYNC_DELAY); + + /* spin waiting for the command to complete */ + for (i = 0; i < AAC_IMMEDIATE_TIMEOUT * 1000; i++) { + if (AAC_GET_ISTATUS(sc) & AAC_DB_SYNC_COMMAND); + break; + DELAY(1000); + } + if (i == AAC_IMMEDIATE_TIMEOUT * 1000) { + AAC_UNLOCK(sc, lock); + return (EIO); + } + + /* clear the completion flag */ + AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND); + + /* get the command status */ + status = AAC_GET_MAILBOXSTATUS(sc); + AAC_UNLOCK(sc, lock); + if (sp != NULL) + *sp = status; + return (0); /* check command return status? */ +} + +/* + * Send a synchronous FIB to the controller and wait for a result. + */ +int +aac_sync_fib(sc, command, xferstate, data, datasize, result, resultsize) + struct aac_softc *sc; + u_int32_t command; + u_int32_t xferstate; + void *data; + u_int16_t datasize; + void *result; + u_int16_t *resultsize; +{ + struct aac_fib *fib = &sc->sc_common->ac_sync_fib; + + if (datasize > AAC_FIB_DATASIZE) + return (EINVAL); + + /* + * Set up the sync FIB + */ + fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED | + AAC_FIBSTATE_INITIALISED | AAC_FIBSTATE_EMPTY; + fib->Header.XferState |= xferstate; + fib->Header.Command = command; + fib->Header.StructType = AAC_FIBTYPE_TFIB; + fib->Header.Size = sizeof fib + datasize; + fib->Header.SenderSize = sizeof *fib; + fib->Header.SenderFibAddress = (u_int32_t)fib; + fib->Header.ReceiverFibAddress = + sc->sc_common_busaddr + offsetof(struct aac_common, ac_sync_fib); + + /* + * Copy in data. + */ + if (data != NULL) { + bcopy(data, fib->data, datasize); + fib->Header.XferState |= + AAC_FIBSTATE_FROMHOST | AAC_FIBSTATE_NORM; + } + + /* + * Give the FIB to the controller, wait for a response. + */ + if (aac_sync_command(sc, AAC_MONKER_SYNCFIB, + fib->Header.ReceiverFibAddress, 0, 0, 0, NULL)) { + return (EIO); + } + + /* + * Copy out the result + */ + if (result != NULL) { + *resultsize = fib->Header.Size - sizeof fib->Header; + bcopy(fib->data, result, *resultsize); + } + return (0); +} + +void +aacminphys(bp) + struct buf *bp; +{ +#if 0 + u_int8_t *buf = bp->b_data; + paddr_t pa; + long off; +#endif + + AAC_DPRINTF(AAC_D_MISC, ("aacminphys(0x%x) ", bp)); + +#if 1 +#if 0 /* As this is way more than MAXPHYS it's really not necessary. */ + if (bp->b_bcount > ((GDT_MAXOFFSETS - 1) * PAGE_SIZE)) + bp->b_bcount = ((GDT_MAXOFFSETS - 1) * PAGE_SIZE); +#endif +#else + for (off = PAGE_SIZE, pa = vtophys(buf); off < bp->b_bcount; + off += PAGE_SIZE) + if (pa + off != vtophys(buf + off)) { + bp->b_bcount = off; + break; + } +#endif + minphys(bp); +} + +/* + * Read the current firmware status word. + */ +int +aac_sa_get_fwstatus(sc) + struct aac_softc *sc; +{ + return (AAC_GETREG4(sc, AAC_SA_FWSTATUS)); +} + +int +aac_rx_get_fwstatus(sc) + struct aac_softc *sc; +{ + return (AAC_GETREG4(sc, AAC_RX_FWSTATUS)); +} + +/* + * Notify the controller of a change in a given queue + */ + +void +aac_sa_qnotify(sc, qbit) + struct aac_softc *sc; + int qbit; +{ + AAC_SETREG2(sc, AAC_SA_DOORBELL1_SET, qbit); +} + +void +aac_rx_qnotify(sc, qbit) + struct aac_softc *sc; + int qbit; +{ + AAC_SETREG4(sc, AAC_RX_IDBR, qbit); +} + +/* + * Get the interrupt reason bits + */ +int +aac_sa_get_istatus(sc) + struct aac_softc *sc; +{ + return (AAC_GETREG2(sc, AAC_SA_DOORBELL0)); +} + +int +aac_rx_get_istatus(sc) + struct aac_softc *sc; +{ + return (AAC_GETREG4(sc, AAC_RX_ODBR)); +} + +/* + * Clear some interrupt reason bits + */ +void +aac_sa_clear_istatus(sc, mask) + struct aac_softc *sc; + int mask; +{ + AAC_SETREG2(sc, AAC_SA_DOORBELL0_CLEAR, mask); +} + +void +aac_rx_clear_istatus(sc, mask) + struct aac_softc *sc; + int mask; +{ + AAC_SETREG4(sc, AAC_RX_ODBR, mask); +} + +/* + * Populate the mailbox and set the command word + */ +void +aac_sa_set_mailbox(sc, command, arg0, arg1, arg2, arg3) + struct aac_softc *sc; + u_int32_t command; + u_int32_t arg0; + u_int32_t arg1; + u_int32_t arg2; + u_int32_t arg3; +{ + AAC_SETREG4(sc, AAC_SA_MAILBOX, command); + AAC_SETREG4(sc, AAC_SA_MAILBOX + 4, arg0); + AAC_SETREG4(sc, AAC_SA_MAILBOX + 8, arg1); + AAC_SETREG4(sc, AAC_SA_MAILBOX + 12, arg2); + AAC_SETREG4(sc, AAC_SA_MAILBOX + 16, arg3); +} + +void +aac_rx_set_mailbox(sc, command, arg0, arg1, arg2, arg3) + struct aac_softc *sc; + u_int32_t command; + u_int32_t arg0; + u_int32_t arg1; + u_int32_t arg2; + u_int32_t arg3; +{ + AAC_SETREG4(sc, AAC_RX_MAILBOX, command); + AAC_SETREG4(sc, AAC_RX_MAILBOX + 4, arg0); + AAC_SETREG4(sc, AAC_RX_MAILBOX + 8, arg1); + AAC_SETREG4(sc, AAC_RX_MAILBOX + 12, arg2); + AAC_SETREG4(sc, AAC_RX_MAILBOX + 16, arg3); +} + +/* + * Fetch the immediate command status word + */ +int +aac_sa_get_mailboxstatus(sc) + struct aac_softc *sc; +{ + return (AAC_GETREG4(sc, AAC_SA_MAILBOX)); +} + +int +aac_rx_get_mailboxstatus(sc) + struct aac_softc *sc; +{ + return (AAC_GETREG4(sc, AAC_RX_MAILBOX)); +} + +/* + * Set/clear interrupt masks + */ +void +aac_sa_set_interrupts(sc, enable) + struct aac_softc *sc; + int enable; +{ + if (enable) + AAC_SETREG2((sc), AAC_SA_MASK0_CLEAR, AAC_DB_INTERRUPTS); + else + AAC_SETREG2((sc), AAC_SA_MASK0_SET, ~0); +} + +void +aac_rx_set_interrupts(sc, enable) + struct aac_softc *sc; + int enable; +{ + if (enable) + AAC_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INTERRUPTS); + else + AAC_SETREG4(sc, AAC_RX_OIMR, ~0); +} + +struct aac_ccb * +aac_get_ccb(sc, flags) + struct aac_softc *sc; + int flags; +{ + struct aac_ccb *ccb; + aac_lock_t lock; + + AAC_DPRINTF(AAC_D_QUEUE, ("aac_get_ccb(%p, 0x%x) ", sc, flags)); + + lock = AAC_LOCK(sc); + + for (;;) { + ccb = TAILQ_FIRST(&sc->sc_free_ccb); + if (ccb != NULL) + break; + if (flags & SCSI_NOSLEEP) + goto bail_out; + tsleep(&sc->sc_free_ccb, PRIBIO, "aac_ccb", 0); + } + + TAILQ_REMOVE(&sc->sc_free_ccb, ccb, ac_chain); + + /* initialise the command/FIB */ + ccb->ac_sgtable = NULL; + ccb->ac_flags = 0; + ccb->ac_fib->Header.XferState = AAC_FIBSTATE_EMPTY; + ccb->ac_fib->Header.StructType = AAC_FIBTYPE_TFIB; + ccb->ac_fib->Header.Flags = 0; + ccb->ac_fib->Header.SenderSize = sizeof(struct aac_fib); + + /* + * These are duplicated in aac_start to cover the case where an + * intermediate stage may have destroyed them. They're left + * initialised here for debugging purposes only. + */ + ccb->ac_fib->Header.SenderFibAddress = (u_int32_t)ccb->ac_fib; + ccb->ac_fib->Header.ReceiverFibAddress = ccb->ac_fibphys; + + bail_out: + AAC_UNLOCK(sc, lock); + return (ccb); +} + +void +aac_free_ccb(sc, ccb) + struct aac_softc *sc; + struct aac_ccb *ccb; +{ + aac_lock_t lock; + + AAC_DPRINTF(AAC_D_QUEUE, ("aac_free_ccb(%p, %p) ", sc, ccb)); + + lock = AAC_LOCK(sc); + + TAILQ_INSERT_HEAD(&sc->sc_free_ccb, ccb, ac_chain); + + /* If the free list was empty, wake up potential waiters. */ + if (TAILQ_NEXT(ccb, ac_chain) == NULL) + wakeup(&sc->sc_free_ccb); + + AAC_UNLOCK(sc, lock); +} + +void +aac_enqueue_ccb(sc, ccb) + struct aac_softc *sc; + struct aac_ccb *ccb; +{ + AAC_DPRINTF(AAC_D_QUEUE, ("aac_enqueue_ccb(%p, %p) ", sc, ccb)); + + TAILQ_INSERT_TAIL(&sc->sc_ccbq, ccb, ac_chain); + aac_start_ccbs(sc); +} + +void +aac_start_ccbs(sc) + struct aac_softc *sc; +{ + struct aac_ccb *ccb; + + AAC_DPRINTF(AAC_D_QUEUE, ("aac_start_ccbs(%p) ", sc)); + + while ((ccb = TAILQ_FIRST(&sc->sc_ccbq)) != NULL) { + if (ccb->ac_flags & AAC_ACF_WATCHDOG) + untimeout(aac_watchdog, ccb); + + if (aac_exec_ccb(ccb) == 0) { + ccb->ac_flags |= AAC_ACF_WATCHDOG; + timeout(aac_watchdog, ccb, + (AAC_WATCH_TIMEOUT * hz) / 1000); + break; + } + TAILQ_REMOVE(&sc->sc_ccbq, ccb, ac_chain); + + if ((ccb->ac_xs->flags & SCSI_POLL) == 0) + timeout(aac_timeout, ccb, + (ccb->ac_timeout * hz) / 1000); + } +} + +int +aac_exec_ccb(ccb) + struct aac_ccb *ccb; +{ + struct scsi_xfer *xs = ccb->ac_xs; + struct scsi_link *link = xs->sc_link; + u_int8_t target = link->target; + int i; + struct aac_fib *fib; + struct aac_blockread *br; + struct aac_blockwrite *bw; + bus_dmamap_t xfer; + + AAC_DPRINTF(AAC_D_CMD, ("aac_exec_ccb(%p, %p) ", xs, ccb)); + + /* build the FIB */ + fib = ccb->ac_fib; + fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED | + AAC_FIBSTATE_INITIALISED | AAC_FIBSTATE_FROMHOST | + AAC_FIBSTATE_REXPECTED | AAC_FIBSTATE_NORM; + fib->Header.Command = ContainerCommand; + fib->Header.Size = sizeof(struct aac_fib_header); + + switch (xs->cmd->opcode) { + case PREVENT_ALLOW: + case SYNCHRONIZE_CACHE: + if (xs->cmd->opcode == PREVENT_ALLOW) { + /* XXX PREVENT_ALLOW support goes here */ + } else { + AAC_DPRINTF(AAC_D_CMD, + ("SYNCHRONIZE CACHE tgt %d ", target)); + } + break; + + case WRITE_COMMAND: + case WRITE_BIG: + bw = (struct aac_blockwrite *)&fib->data[0]; + bw->Command = VM_CtBlockWrite; + bw->ContainerId = target; + bw->BlockNumber = ccb->ac_blockno; + bw->ByteCount = ccb->ac_blockcnt * DEV_BSIZE; + bw->Stable = CUNSTABLE; /* XXX what's appropriate here? */ + fib->Header.Size += sizeof(struct aac_blockwrite); + ccb->ac_sgtable = &bw->SgMap; + break; + + case READ_COMMAND: + case READ_BIG: + br = (struct aac_blockread *)&fib->data[0]; + br->Command = VM_CtBlockRead; + br->ContainerId = target; + br->BlockNumber = ccb->ac_blockno; + br->ByteCount = ccb->ac_blockcnt * DEV_BSIZE; + fib->Header.Size += sizeof(struct aac_blockread); + ccb->ac_sgtable = &br->SgMap; + break; + } + + if (xs->cmd->opcode != PREVENT_ALLOW && + xs->cmd->opcode != SYNCHRONIZE_CACHE) { + xfer = ccb->ac_dmamap_xfer; + ccb->ac_sgtable->SgCount = xfer->dm_nsegs; + for (i = 0; i < xfer->dm_nsegs; i++) { + ccb->ac_sgtable->SgEntry[i].SgAddress = + xfer->dm_segs[i].ds_addr; + ccb->ac_sgtable->SgEntry[i].SgByteCount = + xfer->dm_segs[i].ds_len; + AAC_DPRINTF(AAC_D_IO, + ("#%d va %p pa %p len %x\n", i, buf, + xfer->dm_segs[i].ds_addr, + xfer->dm_segs[i].ds_len)); + } + + /* update the FIB size for the s/g count */ + fib->Header.Size += xfer->dm_nsegs * + sizeof(struct aac_sg_entry); + } + + aac_start(ccb); + + xs->error = XS_NOERROR; + xs->resid = 0; + return (1); +} + +/******************************************************************************** + * Deliver a command to the controller; allocate controller resources at the + * last moment when possible. + */ +int +aac_start(struct aac_ccb *ccb) +{ + struct aac_softc *sc = ccb->ac_xs->sc_link->adapter_softc; + +#if 0 + /* get the command mapped */ + aac_map_command(ccb); +#endif + + /* fix up the address values */ + ccb->ac_fib->Header.SenderFibAddress = (u_int32_t)ccb->ac_fib; + ccb->ac_fib->Header.ReceiverFibAddress = ccb->ac_fibphys; + + /* save a pointer to the command for speedy reverse-lookup */ + ccb->ac_fib->Header.SenderData = (u_int32_t)ccb; /* XXX ack, sizing */ + + /* put the FIB on the outbound queue */ + if (aac_enqueue_fib(sc, AAC_ADAP_NORM_CMD_QUEUE, + ccb->ac_fib->Header.Size, ccb->ac_fib->Header.ReceiverFibAddress)) + return (EBUSY); + + return (0); +} + +/* + * Map a command into controller-visible space. + */ +int +aac_map_command(struct aac_ccb *ccb) +{ + struct scsi_xfer *xs = ccb->ac_xs; + struct aac_softc *sc = xs->sc_link->adapter_softc; + int error; + +#if 0 + /* don't map more than once */ + if (ccb->ac_flags & AAC_CMD_MAPPED) + return; +#endif + + if (xs->datalen != 0) { + error = bus_dmamap_load(sc->sc_dmat, ccb->ac_dmamap_xfer, + xs->data, xs->datalen, NULL, + (xs->flags & SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : + BUS_DMA_WAITOK); + if (error) { + printf("%s: aac_scsi_cmd: ", sc->sc_dev.dv_xname); + if (error == EFBIG) + printf("more than %d dma segs\n", + AAC_MAXSGENTRIES); + else + printf("error %d loading dma map\n", error); + return (error); + } + + bus_dmamap_sync(sc->sc_dmat, ccb->ac_dmamap_xfer, + (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD : + BUS_DMASYNC_PREWRITE); + } + +#if 0 + ccb->ac_flags |= AAC_CMD_MAPPED; +#endif + return (0); +} + +/* + * Unmap a command from controller-visible space. + */ +void +aac_unmap_command(struct aac_ccb *ccb) +{ + struct scsi_xfer *xs = ccb->ac_xs; + struct aac_softc *sc = xs->sc_link->adapter_softc; + +#if 0 + if (!(ccb->ac_flags & AAC_CMD_MAPPED)) + return; +#endif + + if (xs->datalen != 0) { + bus_dmamap_sync(sc->sc_dmat, ccb->ac_dmamap_xfer, + (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD : + BUS_DMASYNC_POSTWRITE); + + bus_dmamap_unload(sc->sc_dmat, ccb->ac_dmamap_xfer); + } +#if 0 + ccb->ac_flags &= ~AAC_CMD_MAPPED; +#endif +} + +void +aac_timeout(arg) + void *arg; +{ + struct aac_ccb *ccb = arg; + struct scsi_link *link = ccb->ac_xs->sc_link; + struct aac_softc *sc = link->adapter_softc; + aac_lock_t lock; + + sc_print_addr(link); + printf("timed out\n"); + + /* XXX Test for multiple timeouts */ + + ccb->ac_xs->error = XS_TIMEOUT; + lock = AAC_LOCK(sc); + aac_enqueue_ccb(sc, ccb); + AAC_UNLOCK(sc, lock); +} + +void +aac_watchdog(arg) + void *arg; +{ + struct aac_ccb *ccb = arg; + struct scsi_link *link = ccb->ac_xs->sc_link; + struct aac_softc *sc = link->adapter_softc; + aac_lock_t lock; + + lock = AAC_LOCK(sc); + ccb->ac_flags &= ~AAC_ACF_WATCHDOG; + aac_start_ccbs(sc); + AAC_UNLOCK(sc, lock); +} +/* + * Insert a command into the driver queue, either at the front or at the tail. + * It's ok to overload the freelist link as these structures are never on + * the freelist at this time. + */ +void +aac_enqueue(sc, xs, infront) + struct aac_softc *sc; + struct scsi_xfer *xs; + int infront; +{ + if (infront || LIST_FIRST(&sc->sc_queue) == NULL) { + if (LIST_FIRST(&sc->sc_queue) == NULL) + sc->sc_queuelast = xs; + LIST_INSERT_HEAD(&sc->sc_queue, xs, free_list); + return; + } + LIST_INSERT_AFTER(sc->sc_queuelast, xs, free_list); + sc->sc_queuelast = xs; +} + +/* + * Pull a command off the front of the driver queue. + */ +struct scsi_xfer * +aac_dequeue(sc) + struct aac_softc *sc; +{ + struct scsi_xfer *xs; + + xs = LIST_FIRST(&sc->sc_queue); + if (xs == NULL) + return (NULL); + LIST_REMOVE(xs, free_list); + + if (LIST_FIRST(&sc->sc_queue) == NULL) + sc->sc_queuelast = NULL; + + return (xs); +} + +/******************************************************************************** + * Adapter-space FIB queue manipulation + * + * Note that the queue implementation here is a little funky; neither the PI or + * CI will ever be zero. This behaviour is a controller feature. + */ +static struct { + int size; + int notify; +} aac_qinfo[] = { + { AAC_HOST_NORM_CMD_ENTRIES, AAC_DB_COMMAND_NOT_FULL }, + { AAC_HOST_HIGH_CMD_ENTRIES, 0 }, + { AAC_ADAP_NORM_CMD_ENTRIES, AAC_DB_COMMAND_READY }, + { AAC_ADAP_HIGH_CMD_ENTRIES, 0 }, + { AAC_HOST_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_NOT_FULL }, + { AAC_HOST_HIGH_RESP_ENTRIES, 0 }, + { AAC_ADAP_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_READY }, + { AAC_ADAP_HIGH_RESP_ENTRIES, 0 } +}; + +/* + * Atomically insert an entry into the nominated queue, returns 0 on success + * or EBUSY if the queue is full. + * + * XXX Note that it would be more efficient to defer notifying the controller + * in the case where we may be inserting several entries in rapid succession, + * but implementing this usefully is difficult. + */ +int +aac_enqueue_fib(struct aac_softc *sc, int queue, u_int32_t fib_size, + u_int32_t fib_addr) +{ + u_int32_t pi, ci; + int error; + aac_lock_t lock; + + lock = AAC_LOCK(sc); + + /* get the producer/consumer indices */ + pi = sc->sc_queues->qt_qindex[queue][AAC_PRODUCER_INDEX]; + ci = sc->sc_queues->qt_qindex[queue][AAC_CONSUMER_INDEX]; + + /* wrap the queue? */ + if (pi >= aac_qinfo[queue].size) + pi = 0; + + /* check for queue full */ + if ((pi + 1) == ci) { + error = EBUSY; + goto out; + } + + /* populate queue entry */ + (sc->sc_qentries[queue] + pi)->aq_fib_size = fib_size; + (sc->sc_qentries[queue] + pi)->aq_fib_addr = fib_addr; + + /* update producer index */ + sc->sc_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1; + + /* notify the adapter if we know how */ + if (aac_qinfo[queue].notify != 0) + AAC_QNOTIFY(sc, aac_qinfo[queue].notify); + + error = 0; + +out: + AAC_UNLOCK(sc, lock); + return (error); +} + +/* + * Atomically remove one entry from the nominated queue, returns 0 on success + * or ENOENT if the queue is empty. + */ +int +aac_dequeue_fib(struct aac_softc *sc, int queue, u_int32_t *fib_size, + struct aac_fib **fib_addr) +{ + u_int32_t pi, ci; + int error; + aac_lock_t lock; + + lock = AAC_LOCK(sc); + + /* get the producer/consumer indices */ + pi = sc->sc_queues->qt_qindex[queue][AAC_PRODUCER_INDEX]; + ci = sc->sc_queues->qt_qindex[queue][AAC_CONSUMER_INDEX]; + + /* check for queue empty */ + if (ci == pi) { + error = ENOENT; + goto out; + } + + /* wrap the queue? */ + if (ci >= aac_qinfo[queue].size) + ci = 0; + + /* fetch the entry */ + *fib_size = (sc->sc_qentries[queue] + ci)->aq_fib_size; + *fib_addr = + (struct aac_fib *)(sc->sc_qentries[queue] + ci)->aq_fib_addr; + + /* update consumer index */ + sc->sc_queues->qt_qindex[queue][AAC_CONSUMER_INDEX] = ci + 1; + + /* if we have made the queue un-full, notify the adapter */ + if (((pi + 1) == ci) && (aac_qinfo[queue].notify != 0)) + AAC_QNOTIFY(sc, aac_qinfo[queue].notify); + error = 0; + +out: + AAC_UNLOCK(sc, lock); + return (error); +} + +#ifdef AAC_DEBUG +/* + * Print a FIB + */ +void +aac_print_fib(struct aac_softc *sc, struct aac_fib *fib, char *caller) +{ + printf("%s: FIB @ %p\n", caller, fib); + printf(" XferState %b\n", fib->Header.XferState, "\20" + "\1HOSTOWNED" + "\2ADAPTEROWNED" + "\3INITIALISED" + "\4EMPTY" + "\5FROMPOOL" + "\6FROMHOST" + "\7FROMADAP" + "\10REXPECTED" + "\11RNOTEXPECTED" + "\12DONEADAP" + "\13DONEHOST" + "\14HIGH" + "\15NORM" + "\16ASYNC" + "\17PAGEFILEIO" + "\20SHUTDOWN" + "\21LAZYWRITE" + "\22ADAPMICROFIB" + "\23BIOSFIB" + "\24FAST_RESPONSE" + "\25APIFIB\n"); + printf(" Command %d\n", fib->Header.Command); + printf(" StructType %d\n", fib->Header.StructType); + printf(" Flags 0x%x\n", fib->Header.Flags); + printf(" Size %d\n", fib->Header.Size); + printf(" SenderSize %d\n", fib->Header.SenderSize); + printf(" SenderAddress 0x%x\n", fib->Header.SenderFibAddress); + printf(" ReceiverAddress 0x%x\n", fib->Header.ReceiverFibAddress); + printf(" SenderData 0x%x\n", fib->Header.SenderData); + switch(fib->Header.Command) { + case ContainerCommand: { + struct aac_blockread *br = (struct aac_blockread *)fib->data; + struct aac_blockwrite *bw = (struct aac_blockwrite *)fib->data; + struct aac_sg_table *sg = NULL; + int i; + + if (br->Command == VM_CtBlockRead) { + printf(" BlockRead: container %d 0x%x/%d\n", + br->ContainerId, br->BlockNumber, br->ByteCount); + sg = &br->SgMap; + } + if (bw->Command == VM_CtBlockWrite) { + printf(" BlockWrite: container %d 0x%x/%d (%s)\n", + bw->ContainerId, bw->BlockNumber, bw->ByteCount, + bw->Stable == CSTABLE ? "stable" : "unstable"); + sg = &bw->SgMap; + } + if (sg != NULL) { + printf(" %d s/g entries\n", sg->SgCount); + for (i = 0; i < sg->SgCount; i++) + printf(" 0x%08x/%d\n", + sg->SgEntry[i].SgAddress, + sg->SgEntry[i].SgByteCount); + } + break; + } + default: + printf(" %16D\n", fib->data, " "); + printf(" %16D\n", fib->data + 16, " "); + break; + } +} +#endif diff --git a/sys/dev/ic/aac_tables.h b/sys/dev/ic/aac_tables.h new file mode 100644 index 00000000000..ea535ea12ba --- /dev/null +++ b/sys/dev/ic/aac_tables.h @@ -0,0 +1,121 @@ +/* $OpenBSD: aac_tables.h,v 1.1 2000/11/10 09:39:35 niklas Exp $ */ + +/*- + * Copyright (c) 2000 Michael Smith + * Copyright (c) 2000 BSDi + * Copyright (c) 2000 Niklas Hallqvist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: /c/ncvs/src/sys/dev/aac/aac_tables.h,v 1.1 2000/09/13 03:20:34 msmith Exp $ + */ + +/* + * Status codes for block read/write commands, etc. + * + * XXX many of these would not normally be returned, as they are + * relevant only to FSA operations. + */ +static struct aac_code_lookup aac_command_status_table[] = { + { "OK", 0 }, + { "operation not permitted", 1 }, + { "not found", 2 }, + { "I/O error", 5 }, + { "device not configured", 6 }, + { "too big", 7 }, + { "permission denied", 13 }, + { "file exists", 17 }, + { "cross-device link", 18 }, + { "operation not supported by device", 19 }, + { "not a directory", 20 }, + { "is a directory", 21 }, + { "invalid argument", 22 }, + { "file too large", 27 }, + { "no space on device", 28 }, + { "readonly filesystem", 30 }, + { "too many links", 31 }, + { "operation would block", 35 }, + { "file name too long", 63 }, + { "directory not empty", 66 }, + { "quota exceeded", 69 }, + { "stale file handle", 70 }, + { "too many levels of remote in path", 71 }, + { "bad file handle", 10001 }, + { "not sync", 10002 }, + { "bad cookie", 10003 }, + { "operation not supported", 10004 }, + { "too small", 10005 }, + { "server fault", 10006 }, + { "bad type", 10007 }, + { "jukebox", 10008 }, + { "not mounted", 10009 }, + { "in maintenace mode", 10010 }, + { "stale ACL", 10011 }, + { NULL, 0 }, + { "unknown command status", 0 } +}; + +#define AAC_COMMAND_STATUS(x) aac_describe_code(aac_command_status_table, x) + +static struct aac_code_lookup aac_cpu_variant[] = { + { "i960JX", CPUI960_JX }, + { "i960CX", CPUI960_CX }, + { "i960HX", CPUI960_HX }, + { "i960RX", CPUI960_RX }, + { "StrongARM SA110", CPUARM_SA110 }, + { "PowerPC 603e", CPUPPC_603e }, + { "Unknown StrongARM", CPUARM_xxx }, + { "Unknown PowerPC", CPUPPC_xxx }, + { NULL, 0 }, + { "Unknown processor", 0 } +}; + +static struct aac_code_lookup aac_battery_platform[] = { + { "required battery present", PLATFORM_BAT_REQ_PRESENT }, + { "REQUIRED BATTERY NOT PRESENT", PLATFORM_BAT_REQ_NOTPRESENT }, + { "optional battery present", PLATFORM_BAT_OPT_PRESENT }, + { "optional battery not installed", PLATFORM_BAT_OPT_NOTPRESENT }, + { "no battery support", PLATFORM_BAT_NOT_SUPPORTED }, + { NULL, 0 }, + { "unknown battery platform", 0 } +}; + +#if 0 +static struct aac_code_lookup aac_container_types[] = { + { "Volume", CT_VOLUME }, + { "RAID 1 (Mirror)", CT_MIRROR }, + { "RAID 0 (Stripe)", CT_STRIPE }, + { "RAID 5", CT_RAID5 }, + { "SSRW", CT_SSRW }, + { "SSRO", CT_SSRO }, + { "Morph", CT_MORPH }, + { "Passthrough", CT_PASSTHRU }, + { "RAID 4", CT_RAID4 }, + { "RAID 10", CT_RAID10 }, + { "RAID 00", CT_RAID00 }, + { "Volume of Mirrors", CT_VOLUME_OF_MIRRORS }, + { "Pseudo RAID 3", CT_PSEUDO_RAID3 }, + { NULL, 0 }, + { "unknown", 0 } +}; +#endif diff --git a/sys/dev/ic/aacreg.h b/sys/dev/ic/aacreg.h new file mode 100644 index 00000000000..f6a6f7bac32 --- /dev/null +++ b/sys/dev/ic/aacreg.h @@ -0,0 +1,717 @@ +/* $OpenBSD: aacreg.h,v 1.1 2000/11/10 09:39:36 niklas Exp $ */ + +/*- + * Copyright (c) 2000 Michael Smith + * Copyright (c) 2000 Scott Long + * Copyright (c) 2000 BSDi + * Copyright (c) 2000 Niklas Hallqvist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: /c/ncvs/src/sys/dev/aac/aacreg.h,v 1.1 2000/09/13 03:20:34 msmith Exp $ + */ + +/* + * Data structures defining the interface between the driver and the Adaptec + * 'FSA' adapters. Note that many field names and comments here are taken + * verbatim from the Adaptec driver source in order to make comparing the + * two slightly easier. + */ + +/* + * Misc. magic numbers. + */ +#define AAC_MAX_CONTAINERS 64 +#define AAC_BLOCK_SIZE 512 + +/* + * Communications interface. + * + * Where datastructure layouts are closely parallel to the Adaptec sample code, + * retain their naming conventions (for now) to aid in cross-referencing. + */ + +/* + * We establish 4 command queues and matching response queues. Queues must + * be 16-byte aligned, and are sized as follows: + */ +#define AAC_HOST_NORM_CMD_ENTRIES 8 /* cmd adapter->host, normal pri */ +#define AAC_HOST_HIGH_CMD_ENTRIES 4 /* cmd adapter->host, high pri */ +#define AAC_ADAP_NORM_CMD_ENTRIES 512 /* cmd host->adapter, normal pri */ +#define AAC_ADAP_HIGH_CMD_ENTRIES 4 /* cmd host->adapter, high pri */ +#define AAC_HOST_NORM_RESP_ENTRIES 512 /* resp, adapter->host, normal pri */ +#define AAC_HOST_HIGH_RESP_ENTRIES 4 /* resp, adapter->host, high pri */ +#define AAC_ADAP_NORM_RESP_ENTRIES 8 /* resp, host->adapter, normal pri */ +#define AAC_ADAP_HIGH_RESP_ENTRIES 4 /* resp, host->adapter, high pri */ + +#define AAC_TOTALQ_LENGTH \ + (AAC_HOST_HIGH_CMD_ENTRIES + AAC_HOST_NORM_CMD_ENTRIES + \ + AAC_ADAP_HIGH_CMD_ENTRIES + AAC_ADAP_NORM_CMD_ENTRIES + \ + AAC_HOST_HIGH_RESP_ENTRIES + AAC_HOST_NORM_RESP_ENTRIES + \ + AAC_ADAP_HIGH_RESP_ENTRIES + AAC_ADAP_NORM_RESP_ENTRIES) + +#define AAC_QUEUE_COUNT 8 +#define AAC_QUEUE_ALIGN 16 + +struct aac_queue_entry { + u_int32_t aq_fib_size; /* FIB size in bytes */ + u_int32_t aq_fib_addr; /* receiver-space address of the FIB */ +} __attribute__ ((packed)); + +#define AAC_PRODUCER_INDEX 0 +#define AAC_CONSUMER_INDEX 1 + +/* + * Table of queue indices and queues used to communicate with the + * controller. This structure must be aligned to AAC_QUEUE_ALIGN + */ +struct aac_queue_table { + /* queue consumer/producer indexes (layout mandated by adapter) */ + u_int32_t qt_qindex[AAC_QUEUE_COUNT][2]; + + /* queue entry structures (layout mandated by adapter) */ + struct aac_queue_entry qt_HostNormCmdQueue[AAC_HOST_NORM_CMD_ENTRIES]; + struct aac_queue_entry qt_HostHighCmdQueue[AAC_HOST_HIGH_CMD_ENTRIES]; + struct aac_queue_entry qt_AdapNormCmdQueue[AAC_ADAP_NORM_CMD_ENTRIES]; + struct aac_queue_entry qt_AdapHighCmdQueue[AAC_ADAP_HIGH_CMD_ENTRIES]; + struct aac_queue_entry + qt_HostNormRespQueue[AAC_HOST_NORM_RESP_ENTRIES]; + struct aac_queue_entry + qt_HostHighRespQueue[AAC_HOST_HIGH_RESP_ENTRIES]; + struct aac_queue_entry + qt_AdapNormRespQueue[AAC_ADAP_NORM_RESP_ENTRIES]; + struct aac_queue_entry + qt_AdapHighRespQueue[AAC_ADAP_HIGH_RESP_ENTRIES]; +} __attribute__ ((packed)); + +/* + * Adapter Init Structure: this is passed to the adapter with the + * AAC_MONKER_INITSTRUCT command to point it at our control structures. + */ +struct aac_adapter_init { + u_int32_t InitStructRevision; +#define AAC_INIT_STRUCT_REVISION 3 + u_int32_t MiniPortRevision; + u_int32_t FilesystemRevision; + u_int32_t CommHeaderAddress; + u_int32_t FastIoCommAreaAddress; + u_int32_t AdapterFibsPhysicalAddress; + void *AdapterFibsVirtualAddress; + u_int32_t AdapterFibsSize; + u_int32_t AdapterFibAlign; + u_int32_t PrintfBufferAddress; + u_int32_t PrintfBufferSize; + u_int32_t HostPhysMemPages; + u_int32_t HostElapsedSeconds; +} __attribute__((packed)); + +/* + * Shared data types + */ + +/* + * Container types + */ +typedef enum { + CT_NONE = 0, + CT_VOLUME, + CT_MIRROR, + CT_STRIPE, + CT_RAID5, + CT_SSRW, + CT_SSRO, + CT_MORPH, + CT_PASSTHRU, + CT_RAID4, + CT_RAID10, /* stripe of mirror */ + CT_RAID00, /* stripe of stripe */ + CT_VOLUME_OF_MIRRORS, /* volume of mirror */ + CT_PSEUDO_RAID3, /* really raid4 */ +} AAC_FSAVolType; + +/* + * Host-addressable object types + */ +typedef enum { + FT_REG = 1, /* regular file */ + FT_DIR, /* directory */ + FT_BLK, /* "block" device - reserved */ + FT_CHR, /* "character special" device - reserved */ + FT_LNK, /* symbolic link */ + FT_SOCK, /* socket */ + FT_FIFO, /* fifo */ + FT_FILESYS, /* ADAPTEC's "FSA"(tm) filesystem */ + FT_DRIVE, /* phys disk - addressable in scsi by bus/target/lun */ + FT_SLICE, /* virtual disk - raw volume - slice */ + FT_PARTITION, /* FSA part, inside slice, container building block */ + FT_VOLUME, /* Container - Volume Set */ + FT_STRIPE, /* Container - Stripe Set */ + FT_MIRROR, /* Container - Mirror Set */ + FT_RAID5, /* Container - Raid 5 Set */ + FT_DATABASE /* Storage object with "foreign" content manager */ +} AAC_FType; + +/* + * Host-side scatter/gather list for 32-bit commands. + */ +struct aac_sg_entry { + u_int32_t SgAddress; + u_int32_t SgByteCount; +} __attribute__ ((packed)); + +struct aac_sg_table { + u_int32_t SgCount; + struct aac_sg_entry SgEntry[0]; +} __attribute__ ((packed)); + +/* + * Host-side scatter/gather list for 64-bit commands. + */ +struct aac_sg_table64 { + u_int8_t SgCount; + u_int8_t SgSectorsPerPage; + u_int16_t SgByteOffset; + u_int64_t SgEntry[0]; +} __attribute__ ((packed)); + +/* + * Container creation data + */ +struct aac_container_creation { + u_int8_t ViaBuildNumber; + u_int8_t MicroSecond; + u_int8_t Via; /* 1 = FSU, 2 = API, etc. */ + u_int8_t YearsSince1900; + u_int32_t Month:4; /* 1-12 */ + u_int32_t Day:6; /* 1-32 */ + u_int32_t Hour:6; /* 0-23 */ + u_int32_t Minute:6; /* 0-59 */ + u_int32_t Second:6; /* 0-59 */ + u_int64_t ViaAdapterSerialNumber; +} __attribute__ ((packed)); + +struct FsaRevision { + union { + struct { + u_int8_t dash; + u_int8_t type; + u_int8_t minor; + u_int8_t major; + } comp; + u_int32_t ul; + } external; + u_int32_t buildNumber; +} __attribute__((packed)); + +/* + * Adapter Information + */ + +typedef enum { + CPU_NTSIM = 1, + CPU_I960, + CPU_ARM, + CPU_SPARC, + CPU_POWERPC, + CPU_ALPHA, + CPU_P7, + CPU_I960_RX, + CPU__last +} AAC_CpuType; + +typedef enum { + CPUI960_JX = 1, + CPUI960_CX, + CPUI960_HX, + CPUI960_RX, + CPUARM_SA110, + CPUARM_xxx, + CPUPPC_603e, + CPUPPC_xxx, + CPUSUBTYPE__last +} AAC_CpuSubType; + +typedef enum { + PLAT_NTSIM = 1, + PLAT_V3ADU, + PLAT_CYCLONE, + PLAT_CYCLONE_HD, + PLAT_BATBOARD, + PLAT_BATBOARD_HD, + PLAT_YOLO, + PLAT_COBRA, + PLAT_ANAHEIM, + PLAT_JALAPENO, + PLAT_QUEENS, + PLAT_JALAPENO_DELL, + PLAT_POBLANO, + PLAT_POBLANO_OPAL, + PLAT_POBLANO_SL0, + PLAT_POBLANO_SL1, + PLAT_POBLANO_SL2, + PLAT_POBLANO_XXX, + PLAT_JALAPENO_P2, + PLAT_HABANERO, + PLAT__last +} AAC_Platform; + +typedef enum { + OEM_FLAVOR_ADAPTEC = 1, + OEM_FLAVOR_DELL, + OEM_FLAVOR_HP, + OEM_FLAVOR_IBM, + OEM_FLAVOR_CPQ, + OEM_FLAVOR_BRAND_X, + OEM_FLAVOR_BRAND_Y, + OEM_FLAVOR_BRAND_Z, + OEM_FLAVOR__last +} AAC_OemFlavor; + +/* + * XXX the aac-2622 with no battery present reports PLATFORM_BAT_OPT_PRESENT + */ +typedef enum { + PLATFORM_BAT_REQ_PRESENT = 1, /* BATTERY REQUIRED AND PRESENT */ + PLATFORM_BAT_REQ_NOTPRESENT, /* BATTERY REQUIRED AND NOT PRESENT */ + PLATFORM_BAT_OPT_PRESENT, /* BATTERY OPTIONAL AND PRESENT */ + PLATFORM_BAT_OPT_NOTPRESENT, /* BATTERY OPTIONAL AND NOT PRESENT */ + PLATFORM_BAT_NOT_SUPPORTED /* BATTERY NOT SUPPORTED */ +} AAC_BatteryPlatform; + +/* + * Structure used to respond to a RequestAdapterInfo fib. + */ +struct aac_adapter_info { + AAC_Platform PlatformBase; /* adapter type */ + AAC_CpuType CpuArchitecture; /* adapter CPU type */ + AAC_CpuSubType CpuVariant; /* adapter CPU subtype */ + u_int32_t ClockSpeed; /* adapter CPU clockspeed */ + u_int32_t ExecutionMem; /* adapter Execution Memory size */ + u_int32_t BufferMem; /* adapter Data Memory */ + u_int32_t TotalMem; /* adapter Total Memory */ + struct FsaRevision KernelRevision; /* adapter Kernel SW Revision */ + struct FsaRevision MonitorRevision; /* adapter Monitor/Diag SW Rev */ + struct FsaRevision HardwareRevision; /* TDB */ + struct FsaRevision BIOSRevision; /* adapter BIOS Revision */ + u_int32_t ClusteringEnabled; + u_int32_t ClusterChannelMask; + u_int64_t SerialNumber; + AAC_BatteryPlatform batteryPlatform; + u_int32_t SupportedOptions; /* supported features of this ctrlr */ + AAC_OemFlavor OemVariant; +} __attribute__((packed)); + +/* + * Monitor/Kernel interface. + */ + +/* + * Synchronous commands to the monitor/kernel. + */ +#define AAC_MONKER_INITSTRUCT 0x05 +#define AAC_MONKER_SYNCFIB 0x0c + +/* + * Command status values + */ +typedef enum { + ST_OK = 0, + ST_PERM = 1, + ST_NOENT = 2, + ST_IO = 5, + ST_NXIO = 6, + ST_E2BIG = 7, + ST_ACCES = 13, + ST_EXIST = 17, + ST_XDEV = 18, + ST_NODEV = 19, + ST_NOTDIR = 20, + ST_ISDIR = 21, + ST_INVAL = 22, + ST_FBIG = 27, + ST_NOSPC = 28, + ST_ROFS = 30, + ST_MLINK = 31, + ST_WOULDBLOCK = 35, + ST_NAMETOOLONG = 63, + ST_NOTEMPTY = 66, + ST_DQUOT = 69, + ST_STALE = 70, + ST_REMOTE = 71, + ST_BADHANDLE = 10001, + ST_NOT_SYNC = 10002, + ST_BAD_COOKIE = 10003, + ST_NOTSUPP = 10004, + ST_TOOSMALL = 10005, + ST_SERVERFAULT = 10006, + ST_BADTYPE = 10007, + ST_JUKEBOX = 10008, + ST_NOTMOUNTED = 10009, + ST_MAINTMODE = 10010, + ST_STALEACL = 10011 +} AAC_FSAStatus; + +/* + * Volume manager commands + */ +typedef enum _VM_COMMANDS { + VM_Null = 0, + VM_NameServe, + VM_ContainerConfig, + VM_Ioctl, + VM_FilesystemIoctl, + VM_CloseAll, + VM_CtBlockRead, + VM_CtBlockWrite, + VM_SliceBlockRead, /* raw access to configured "storage objects" */ + VM_SliceBlockWrite, + VM_DriveBlockRead, /* raw access to physical devices */ + VM_DriveBlockWrite, + VM_EnclosureMgt, /* enclosure management */ + VM_Unused, /* used to be diskset management */ + VM_CtBlockVerify, + VM_CtPerf, /* performance test */ + VM_CtBlockRead64, + VM_CtBlockWrite64, + VM_CtBlockVerify64, +} AAC_VMCommand; + +/* + * "Mountable object" + */ +struct aac_mntobj { + u_int32_t ObjectId; + char FileSystemName[16]; + struct aac_container_creation CreateInfo; + u_int32_t Capacity; + AAC_FSAVolType VolType; + AAC_FType ObjType; + u_int32_t ContentState; +#define AAC_FSCS_READONLY 0x0002 /* XXX need more information than this */ + union { + u_int32_t pad[8]; + } ObjExtension; + u_int32_t AlterEgoId; +} __attribute__ ((packed)); + +struct aac_mntinfo { + AAC_VMCommand Command; + AAC_FType MntType; + u_int32_t MntCount; +} __attribute__ ((packed)); + +struct aac_mntinforesponse { + AAC_FSAStatus Status; + AAC_FType MntType; + u_int32_t MntRespCount; + struct aac_mntobj MntTable[1]; +} __attribute__ ((packed)); + +/* + * Write 'stability' options. + */ +typedef enum { + CSTABLE = 1, + CUNSTABLE +} AAC_CacheLevel; + +/* + * Commit level response for a write request. + */ +typedef enum { + CMFILE_SYNC_NVRAM = 1, + CMDATA_SYNC_NVRAM, + CMFILE_SYNC, + CMDATA_SYNC, + CMUNSTABLE +} AAC_CommitLevel; + +/* + * Block read/write operations. + * These structures are packed into the 'data' area in the FIB. + */ + +struct aac_blockread { + AAC_VMCommand Command; /* not FSACommand! */ + u_int32_t ContainerId; + u_int32_t BlockNumber; + u_int32_t ByteCount; + struct aac_sg_table SgMap; /* variable size */ +} __attribute__ ((packed)); + +struct aac_blockread_response { + AAC_FSAStatus Status; + u_int32_t ByteCount; +} __attribute__ ((packed)); + +struct aac_blockwrite { + AAC_VMCommand Command; /* not FSACommand! */ + u_int32_t ContainerId; + u_int32_t BlockNumber; + u_int32_t ByteCount; + AAC_CacheLevel Stable; + struct aac_sg_table SgMap; /* variable size */ +} __attribute__ ((packed)); + +struct aac_blockwrite_response { + AAC_FSAStatus Status; + u_int32_t ByteCount; + AAC_CommitLevel Committed; +} __attribute__ ((packed)); + +/* + * Register definitions for the Adaptec AAC-364 'Jalapeno I/II' adapters, based + * on the SA110 'StrongArm'. + */ + +/* doorbell 0 (adapter->host) */ +#define AAC_SA_DOORBELL0_CLEAR 0x98 +#define AAC_SA_DOORBELL0_SET 0x9c +#define AAC_SA_DOORBELL0 0x9c +#define AAC_SA_MASK0_CLEAR 0xa0 +#define AAC_SA_MASK0_SET 0xa4 + +/* doorbell 1 (host->adapter) */ +#define AAC_SA_DOORBELL1_CLEAR 0x9a +#define AAC_SA_DOORBELL1_SET 0x9e +#define AAC_SA_MASK1_CLEAR 0xa2 +#define AAC_SA_MASK1_SET 0xa6 + +/* mailbox (20 bytes) */ +#define AAC_SA_MAILBOX 0xa8 +#define AAC_SA_FWSTATUS 0xc4 + +/* + * Register definitions for the Adaptec 'Pablano' adapters, based on the + * i960Rx, and other related adapters. + */ + +#define AAC_RX_IDBR 0x20 /* inbound doorbell */ +#define AAC_RX_IISR 0x24 /* inbound interrupt status */ +#define AAC_RX_IIMR 0x28 /* inbound interrupt mask */ +#define AAC_RX_ODBR 0x2c /* outbound doorbell */ +#define AAC_RX_OISR 0x30 /* outbound interrupt status */ +#define AAC_RX_OIMR 0x34 /* outbound interrupt mask */ + +#define AAC_RX_MAILBOX 0x50 /* mailbox (20 bytes) */ +#define AAC_RX_FWSTATUS 0x6c + +/* + * Common bit definitions for the doorbell registers. + */ + +/* + * Status bits in the doorbell registers. + */ +#define AAC_DB_SYNC_COMMAND (1<<0) /* send/completed synchronous FIB */ +#define AAC_DB_COMMAND_READY (1<<1) /* posted one or more commands */ +#define AAC_DB_RESPONSE_READY (1<<2) /* one or more commands complete */ +#define AAC_DB_COMMAND_NOT_FULL (1<<3) /* command queue not full */ +#define AAC_DB_RESPONSE_NOT_FULL (1<<4) /* response queue not full */ + +/* + * The adapter can request the host print a message by setting the + * DB_PRINTF flag in DOORBELL0. The driver responds by collecting the + * message from the printf buffer, clearing the DB_PRINTF flag in + * DOORBELL0 and setting it in DOORBELL1. + * (ODBR and IDBR respectively for the i960Rx adapters) + */ +#define AAC_DB_PRINTF (1<<5) + +/* + * Mask containing the interrupt bits we care about. We don't anticipate + * (or want) interrupts not in this mask. + */ +#define AAC_DB_INTERRUPTS \ + (AAC_DB_COMMAND_READY | AAC_DB_RESPONSE_READY | AAC_DB_PRINTF) + +/* + * Queue names + * + * Note that we base these at 0 in order to use them as array indices. Adaptec + * used base 1 for some unknown reason, and sorted them in a different order. + */ +#define AAC_HOST_NORM_CMD_QUEUE 0 +#define AAC_HOST_HIGH_CMD_QUEUE 1 +#define AAC_ADAP_NORM_CMD_QUEUE 2 +#define AAC_ADAP_HIGH_CMD_QUEUE 3 +#define AAC_HOST_NORM_RESP_QUEUE 4 +#define AAC_HOST_HIGH_RESP_QUEUE 5 +#define AAC_ADAP_NORM_RESP_QUEUE 6 +#define AAC_ADAP_HIGH_RESP_QUEUE 7 + +/* + * List structure used to chain FIBs (used by the adapter - we hang FIBs off + * our private command structure and don't touch these) + */ +struct aac_fib_list_entry { + struct fib_list_entry *Flink; + struct fib_list_entry *Blink; +} __attribute__((packed)); + +/* + * FIB (FSA Interface Block?); this is the datastructure passed between the + * host and adapter. + */ +struct aac_fib_header { + u_int32_t XferState; + u_int16_t Command; + u_int8_t StructType; + u_int8_t Flags; + u_int16_t Size; + u_int16_t SenderSize; + u_int32_t SenderFibAddress; + u_int32_t ReceiverFibAddress; + u_int32_t SenderData; + union { + struct { + u_int32_t ReceiverTimeStart; + u_int32_t ReceiverTimeDone; + } _s; + struct aac_fib_list_entry FibLinks; + } _u; +} __attribute__((packed)); + +#define AAC_FIB_DATASIZE (512 - sizeof(struct aac_fib_header)) + +struct aac_fib { + struct aac_fib_header Header; + u_int8_t data[AAC_FIB_DATASIZE]; +} __attribute__((packed)); + +/* + * FIB commands + */ +typedef enum { + TestCommandResponse = 1, + TestAdapterCommand = 2, + + /* lowlevel and comm commands */ + LastTestCommand = 100, + ReinitHostNormCommandQueue = 101, + ReinitHostHighCommandQueue = 102, + ReinitHostHighRespQueue = 103, + ReinitHostNormRespQueue = 104, + ReinitAdapNormCommandQueue = 105, + ReinitAdapHighCommandQueue = 107, + ReinitAdapHighRespQueue = 108, + ReinitAdapNormRespQueue = 109, + InterfaceShutdown = 110, + DmaCommandFib = 120, + StartProfile = 121, + TermProfile = 122, + SpeedTest = 123, + TakeABreakPt = 124, + RequestPerfData = 125, + SetInterruptDefTimer= 126, + SetInterruptDefCount= 127, + GetInterruptDefStatus= 128, + LastCommCommand = 129, + + /* filesystem commands */ + NuFileSystem = 300, + UFS = 301, + HostFileSystem = 302, + LastFileSystemCommand = 303, + + /* Container Commands */ + ContainerCommand = 500, + ContainerCommand64 = 501, + + /* Cluster Commands */ + ClusterCommand = 550, + + /* Scsi Port commands (scsi passthrough) */ + ScsiPortCommand = 600, + + /* misc house keeping and generic adapter initiated commands */ + AifRequest = 700, + CheckRevision = 701, + FsaHostShutdown = 702, + RequestAdapterInfo = 703, + IsAdapterPaused = 704, + SendHostTime = 705, + LastMiscCommand = 706 +} AAC_FibCommands; + +/* + * FIB types + */ +#define AAC_FIBTYPE_TFIB 1 +#define AAC_FIBTYPE_TQE 2 +#define AAC_FIBTYPE_TCTPERF 3 + +/* + * FIB transfer state + */ +#define AAC_FIBSTATE_HOSTOWNED (1<<0) /* owned by the host */ +#define AAC_FIBSTATE_ADAPTEROWNED (1<<1) /* owned by the adapter */ +#define AAC_FIBSTATE_INITIALISED (1<<2) /* initialised */ +#define AAC_FIBSTATE_EMPTY (1<<3) /* empty */ +#define AAC_FIBSTATE_FROMPOOL (1<<4) /* allocated from pool */ +#define AAC_FIBSTATE_FROMHOST (1<<5) /* sent from the host */ +#define AAC_FIBSTATE_FROMADAP (1<<6) /* sent from the adapter */ +#define AAC_FIBSTATE_REXPECTED (1<<7) /* response is expected */ +#define AAC_FIBSTATE_RNOTEXPECTED (1<<8) /* response is not expected */ +#define AAC_FIBSTATE_DONEADAP (1<<9) /* processed by the adapter */ +#define AAC_FIBSTATE_DONEHOST (1<<10) /* processed by the host */ +#define AAC_FIBSTATE_HIGH (1<<11) /* high priority */ +#define AAC_FIBSTATE_NORM (1<<12) /* normal priority */ +#define AAC_FIBSTATE_ASYNC (1<<13) +#define AAC_FIBSTATE_ASYNCIO (1<<13) /* to be removed */ +#define AAC_FIBSTATE_PAGEFILEIO (1<<14) /* to be removed */ +#define AAC_FIBSTATE_SHUTDOWN (1<<15) +#define AAC_FIBSTATE_LAZYWRITE (1<<16) /* to be removed */ +#define AAC_FIBSTATE_ADAPMICROFIB (1<<17) +#define AAC_FIBSTATE_BIOSFIB (1<<18) +#define AAC_FIBSTATE_FAST_RESPONSE (1<<19) /* fast response capable */ +#define AAC_FIBSTATE_APIFIB (1<<20) + +/* + * FIB error values + */ +#define AAC_ERROR_NORMAL 0x00 +#define AAC_ERROR_PENDING 0x01 +#define AAC_ERROR_FATAL 0x02 +#define AAC_ERROR_INVALID_QUEUE 0x03 +#define AAC_ERROR_NOENTRIES 0x04 +#define AAC_ERROR_SENDFAILED 0x05 +#define AAC_ERROR_INVALID_QUEUE_PRIORITY 0x06 +#define AAC_ERROR_FIB_ALLOCATION_FAILED 0x07 +#define AAC_ERROR_FIB_DEALLOCATION_FAILED 0x08 + +/* + * Adapter Status Register + * + * Phase Staus mailbox is 32bits: + * <31:16> = Phase Status + * <15:0> = Phase + * + * The adapter reports its present state through the phase. Only + * a single phase should be ever be set. Each phase can have multiple + * phase status bits to provide more detailed information about the + * state of the adapter. + */ +#define AAC_SELF_TEST_FAILED 0x00000004 +#define AAC_UP_AND_RUNNING 0x00000080 +#define AAC_KERNEL_PANIC 0x00000100 diff --git a/sys/dev/ic/aacvar.h b/sys/dev/ic/aacvar.h new file mode 100644 index 00000000000..65c9aee3eea --- /dev/null +++ b/sys/dev/ic/aacvar.h @@ -0,0 +1,341 @@ +/* $OpenBSD: aacvar.h,v 1.1 2000/11/10 09:39:36 niklas Exp $ */ + +/*- + * Copyright (c) 2000 Michael Smith + * Copyright (c) 2000 BSDi + * Copyright (c) 2000 Niklas Hallqvist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: /c/ncvs/src/sys/dev/aac/aacvar.h,v 1.1 2000/09/13 03:20:34 msmith Exp $ + */ + +/* + * This driver would not have rewritten for OpenBSD if it was not for the + * hardware dontion from Nocom. I want to thank them for their support. + * Of course, credit should go to Mike Smith for the original work he did + * in the FreeBSD driver where I found lots of inspiration. + * - Niklas Hallqvist + */ + +/* Debugging */ +#ifdef AAC_DEBUG +#define AAC_DPRINTF(mask, args) if (aac_debug & (mask)) printf args +#define AAC_D_INTR 0x01 +#define AAC_D_MISC 0x02 +#define AAC_D_CMD 0x04 +#define AAC_D_QUEUE 0x08 +#define AAC_D_IO 0x10 +extern int aac_debug; + +#define AAC_PRINT_FIB(sc, fib) aac_print_fib((sc), (fib), __FUNCTION__) +#else +#define AAC_DPRINTF(mask, args) +#define AAC_PRINT_FIB(sc, fib) +#endif + +struct aac_code_lookup { + char *string; + u_int32_t code; +}; + +struct aac_softc; + +/* + * We allocate a small set of FIBs for the adapter to use to send us messages. + */ +#define AAC_ADAPTER_FIBS 8 + +/* + * Firmware messages are passed in the printf buffer. + */ +#define AAC_PRINTF_BUFSIZE 256 + +/* + * We wait this many seconds for the adapter to come ready if it is still + * booting. + */ +#define AAC_BOOT_TIMEOUT (3 * 60) + +/* + * Wait this long for a lost interrupt to get detected. + */ +#define AAC_WATCH_TIMEOUT 10000 /* 10000 * 1ms = 10s */ + +/* + * Timeout for immediate commands. + */ +#define AAC_IMMEDIATE_TIMEOUT 30 + +/* + * Delay 20ms after the qnotify in sync operations. Experimentally deduced. + */ +#define AAC_SYNC_DELAY 20000 + +/* + * The firmware interface allows for a 16-bit s/g list length. We limit + * ourselves to a reasonable maximum and ensure alignment. + */ +#define AAC_MAXSGENTRIES 64 /* max S/G entries, limit 65535 */ +/* + * We gather a number of adapter-visible items into a single structure. + * + * The ordering of this strucure may be important; we copy the Linux driver: + * + * Adapter FIBs + * Init struct + * Queue headers (Comm Area) + * Printf buffer + * + * In addition, we add: + * Sync Fib + */ +struct aac_common { + /* fibs for the controller to send us messages */ + struct aac_fib ac_fibs[AAC_ADAPTER_FIBS]; + + /* the init structure */ + struct aac_adapter_init ac_init; + + /* arena within which the queue structures are kept */ + u_int8_t ac_qbuf[sizeof(struct aac_queue_table) + AAC_QUEUE_ALIGN]; + + /* buffer for text messages from the controller */ + char ac_printf[AAC_PRINTF_BUFSIZE]; + + /* fib for synchronous commands */ + struct aac_fib ac_sync_fib; +}; + +/* + * Interface operations + */ +struct aac_interface { + int (*aif_get_fwstatus) __P((struct aac_softc *)); + void (*aif_qnotify) __P((struct aac_softc *, int)); + int (*aif_get_istatus) __P((struct aac_softc *)); + void (*aif_set_istatus) __P((struct aac_softc *, int)); + void (*aif_set_mailbox) __P((struct aac_softc *, u_int32_t, + u_int32_t, u_int32_t, u_int32_t, u_int32_t)); + int (*aif_get_mailboxstatus) __P((struct aac_softc *)); + void (*aif_set_interrupts) __P((struct aac_softc *, int)); +}; +extern struct aac_interface aac_rx_interface; +extern struct aac_interface aac_sa_interface; + +#define AAC_GET_FWSTATUS(sc) ((sc)->sc_if.aif_get_fwstatus(sc)) +#define AAC_QNOTIFY(sc, qbit) \ + ((sc)->sc_if.aif_qnotify((sc), (qbit))) +#define AAC_GET_ISTATUS(sc) ((sc)->sc_if.aif_get_istatus(sc)) +#define AAC_CLEAR_ISTATUS(sc, mask) \ + ((sc)->sc_if.aif_set_istatus((sc), (mask))) +#define AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3) \ + do { \ + ((sc)->sc_if.aif_set_mailbox((sc), (command), (arg0), \ + (arg1), (arg2), (arg3))); \ + } while(0) +#define AAC_GET_MAILBOXSTATUS(sc) \ + ((sc)->sc_if.aif_get_mailboxstatus(sc)) +#define AAC_MASK_INTERRUPTS(sc) \ + ((sc)->sc_if.aif_set_interrupts((sc), 0)) +#define AAC_UNMASK_INTERRUPTS(sc) \ + ((sc)->sc_if.aif_set_interrupts((sc), 1)) + +#define AAC_SETREG4(sc, reg, val) \ + bus_space_write_4((sc)->sc_memt, (sc)->sc_memh, (reg), (val)) +#define AAC_GETREG4(sc, reg) \ + bus_space_read_4((sc)->sc_memt, (sc)->sc_memh, (reg)) +#define AAC_SETREG2(sc, reg, val) \ + bus_space_write_2((sc)->sc_memt, (sc)->sc_memh, (reg), (val)) +#define AAC_GETREG2(sc, reg) \ + bus_space_read_2((sc)->sc_memt, (sc)->sc_memh, (reg)) +#define AAC_SETREG1(sc, reg, val) \ + bus_space_write_1((sc)->sc_memt, (sc)->sc_memh, (reg), (val)) +#define AAC_GETREG1(sc, reg) \ + bus_space_read_1((sc)->sc_memt, (sc)->sc_memh, (reg)) + +/* + * Per-container data structure + */ +struct aac_container +{ + struct aac_mntobj co_mntobj; + struct device co_disk; +}; + +/* + * A command contol block, one for each corresponding command index of the + * controller. + */ +struct aac_ccb { + TAILQ_ENTRY(aac_ccb) ac_chain; + struct scsi_xfer *ac_xs; + struct aac_fib *ac_fib; /* FIB associated with this command */ + bus_addr_t ac_fibphys; /* bus address of the FIB */ + bus_dmamap_t ac_dmamap_xfer; + struct aac_sg_table *ac_sgtable;/* pointer to s/g table in command */ + int ac_timeout; + u_int32_t ac_blockno; + u_int32_t ac_blockcnt; + u_int8_t ac_flags; +#define AAC_ACF_WATCHDOG 0x1 +#define AAC_ACF_COMPLETED 0x2 +}; + +/* + * Per-controller structure. + */ +struct aac_softc { + struct device sc_dev; + void *sc_ih; + struct scsi_link sc_link; /* Virtual SCSI bus for cache devs */ + + bus_space_tag_t sc_memt; + bus_space_handle_t sc_memh; + bus_dma_tag_t sc_dmat; /* parent DMA tag */ + + /* controller features, limits and status */ + int sc_state; +#define AAC_STATE_SUSPEND (1<<0) +#define AAC_STATE_OPEN (1<<1) +#define AAC_STATE_INTERRUPTS_ON (1<<2) +#define AAC_STATE_AIF_SLEEPER (1<<3) + struct FsaRevision sc_revision; + + int sc_hwif; /* controller hardware interface */ +#define AAC_HWIF_I960RX 0 +#define AAC_HWIF_STRONGARM 1 + + struct aac_common *sc_common; + u_int32_t sc_common_busaddr; + struct aac_interface sc_if; + + /* XXX This should really be dynamic. It is very wasteful now. */ + struct aac_ccb sc_ccbs[AAC_ADAP_NORM_CMD_ENTRIES]; + TAILQ_HEAD(, aac_ccb) sc_free_ccb, sc_ccbq; + /* commands on hold for controller resources */ + TAILQ_HEAD(, aac_ccb) sc_ready; + /* commands which have been returned by the controller */ + TAILQ_HEAD(, aac_ccb) sc_completed; + LIST_HEAD(, scsi_xfer) sc_queue; + struct scsi_xfer *sc_queuelast; + + /* command management */ + struct aac_queue_table *sc_queues; + struct aac_queue_entry *sc_qentries[AAC_QUEUE_COUNT]; + + struct { + u_int8_t hd_present; + u_int8_t hd_is_logdrv; + u_int8_t hd_is_arraydrv; + u_int8_t hd_is_master; + u_int8_t hd_is_parity; + u_int8_t hd_is_hotfix; + u_int8_t hd_master_no; + u_int8_t hd_lock; + u_int8_t hd_heads; + u_int8_t hd_secs; + u_int16_t hd_devtype; + u_int32_t hd_size; + u_int8_t hd_ldr_no; + u_int8_t hd_rw_attribs; + u_int32_t hd_start_sec; + } sc_hdr[AAC_MAX_CONTAINERS]; +}; + +/* XXX These have to become spinlocks in case of SMP */ +#define AAC_LOCK(sc) splbio() +#define AAC_UNLOCK(sc, lock) splx(lock) +typedef int aac_lock_t; + +void aacminphys __P((struct buf *)); +int aac_attach __P((struct aac_softc *)); +int aac_intr __P((void *)); + +#ifdef __GNUC__ +/* These all require correctly aligned buffers */ +static __inline__ void aac_enc16 __P((u_int8_t *, u_int16_t)); +static __inline__ void aac_enc32 __P((u_int8_t *, u_int32_t)); +static __inline__ u_int16_t aac_dec16 __P((u_int8_t *)); +static __inline__ u_int32_t aac_dec32 __P((u_int8_t *)); + +static __inline__ void +aac_enc16(addr, value) + u_int8_t *addr; + u_int16_t value; +{ + *(u_int16_t *)addr = htole16(value); +} + +static __inline__ void +aac_enc32(addr, value) + u_int8_t *addr; + u_int32_t value; +{ + *(u_int32_t *)addr = htole32(value); +} + +static __inline__ u_int16_t +aac_dec16(addr) + u_int8_t *addr; +{ + return letoh16(*(u_int16_t *)addr); +} + +static __inline__ u_int32_t +aac_dec32(addr) + u_int8_t *addr; +{ + return letoh32(*(u_int32_t *)addr); +} + +/* + * Queue primitives + * + * These are broken out individually to make statistics gathering easier. + */ + +static __inline__ void +aac_enqueue_completed(struct aac_ccb *ccb) +{ + struct aac_softc *sc = ccb->ac_xs->sc_link->adapter_softc; + aac_lock_t lock; + + lock = AAC_LOCK(sc); + TAILQ_INSERT_TAIL(&sc->sc_completed, ccb, ac_chain); + AAC_UNLOCK(sc, lock); +} + +static __inline__ struct aac_ccb * +aac_dequeue_completed(struct aac_softc *sc) +{ + struct aac_ccb *ccb; + aac_lock_t lock; + + lock = AAC_LOCK(sc); + if ((ccb = TAILQ_FIRST(&sc->sc_completed)) != NULL) + TAILQ_REMOVE(&sc->sc_completed, ccb, ac_chain); + AAC_UNLOCK(sc, lock); + return (ccb); +} +#endif diff --git a/sys/dev/pci/aac_pci.c b/sys/dev/pci/aac_pci.c new file mode 100644 index 00000000000..def32a49fcf --- /dev/null +++ b/sys/dev/pci/aac_pci.c @@ -0,0 +1,203 @@ +/* $OpenBSD: aac_pci.c,v 1.1 2000/11/10 09:39:36 niklas Exp $ */ + +/*- + * Copyright (c) 2000 Michael Smith + * Copyright (c) 2000 BSDi + * Copyright (c) 2000 Niklas Hallqvist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: /c/ncvs/src/sys/dev/aac/aac_pci.c,v 1.1 2000/09/13 03:20:34 msmith Exp $ + */ + +/* + * This driver would not have rewritten for OpenBSD if it was not for the + * hardware dontion from Nocom. I want to thank them for their support. + * Of course, credit should go to Mike Smith for the original work he did + * in the FreeBSD driver where I found lots of inspiration. + * - Niklas Hallqvist + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/queue.h> + +#include <machine/bus.h> +#include <machine/endian.h> +#include <machine/intr.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#include <dev/pci/pcidevs.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +#include <dev/ic/aacreg.h> +#include <dev/ic/aacvar.h> + +int aac_pci_probe __P((struct device *, void *, void *)); +void aac_pci_attach __P((struct device *, struct device *, void *)); + +struct aac_ident { + u_int16_t vendor; + u_int16_t device; + int hwif; +} aac_identifiers[] = { + { PCI_VENDOR_DELL, PCI_PRODUCT_DELL_PERC_2SI, AAC_HWIF_I960RX }, + { PCI_VENDOR_DELL, PCI_PRODUCT_DELL_PERC_3DI, AAC_HWIF_I960RX }, + { PCI_VENDOR_DELL, PCI_PRODUCT_DELL_PERC_3SI, AAC_HWIF_I960RX }, + { PCI_VENDOR_ADP2, PCI_PRODUCT_ADP2_AAC2622, AAC_HWIF_I960RX }, + { PCI_VENDOR_ADP2, PCI_PRODUCT_ADP2_AAC364, AAC_HWIF_STRONGARM }, + { PCI_VENDOR_ADP2, PCI_PRODUCT_ADP2_AAC3642, AAC_HWIF_STRONGARM }, + { PCI_VENDOR_ADP2, PCI_PRODUCT_ADP2_PERC_2QC, AAC_HWIF_STRONGARM }, + { PCI_VENDOR_ADP2, PCI_PRODUCT_ADP2_PERC_3QC, AAC_HWIF_STRONGARM }, + { PCI_VENDOR_HP, PCI_PRODUCT_HP_NETRAID_4M, AAC_HWIF_STRONGARM }, + { 0, 0, 0 } +}; + +struct cfattach aac_pci_ca = { + sizeof (struct aac_softc), aac_pci_probe, aac_pci_attach +}; + +/* + * Determine whether this is one of our supported adapters. + */ +int +aac_pci_probe(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct pci_attach_args *pa = aux; + struct aac_ident *m; + + for (m = aac_identifiers; m->vendor != 0; m++) + if (m->vendor == PCI_VENDOR(pa->pa_id) && + m->device == PCI_PRODUCT(pa->pa_id)) + return (1); + return (0); +} + +void +aac_pci_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct pci_attach_args *pa = aux; + pci_chipset_tag_t pc = pa->pa_pc; + struct aac_softc *sc = (void *)self; + u_int16_t command; + bus_addr_t membase; + bus_size_t memsize; + pci_intr_handle_t ih; + const char *intrstr; + int state = 0; + struct aac_ident *m; + + printf(": "); + + /* + * Verify that the adapter is correctly set up in PCI space. + */ + command = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + command |= PCI_COMMAND_MASTER_ENABLE; + pci_conf_write(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, command); + command = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + AAC_DPRINTF(AAC_D_MISC, ("pci command status reg 0x08x ")); + if (!(command & PCI_COMMAND_MASTER_ENABLE)) { + printf("can't enable bus-master feature\n"); + goto bail_out; + } + if (!(command & PCI_COMMAND_MEM_ENABLE)) { + printf("memory window not available\n"); + goto bail_out; + } + + /* + * Map control/status registers. + */ + if (pci_mapreg_map(pa, PCI_MAPREG_START, + PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT, 0, &sc->sc_memt, + &sc->sc_memh, &membase, &memsize)) { + printf("can't find mem space\n"); + goto bail_out; + } + state++; + + if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin, pa->pa_intrline, + &ih)) { + printf("couldn't map interrupt\n"); + goto bail_out; + } + intrstr = pci_intr_string(pc, ih); + sc->sc_ih = pci_intr_establish(pc, ih, IPL_BIO, aac_intr, sc, + sc->sc_dev.dv_xname); + if (sc->sc_ih == NULL) { + printf("couldn't establish interrupt"); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + goto bail_out; + } + state++; + if (intrstr != NULL) + printf("%s\n", intrstr); + + sc->sc_dmat = pa->pa_dmat; + + for (m = aac_identifiers; m->vendor != 0; m++) + if (m->vendor == PCI_VENDOR(pa->pa_id) && + m->device == PCI_PRODUCT(pa->pa_id)) { + sc->sc_hwif = m->hwif; + switch(sc->sc_hwif) { + case AAC_HWIF_I960RX: + AAC_DPRINTF(AAC_D_MISC, + ("set hardware up for i960Rx")); + sc->sc_if = aac_rx_interface; + break; + + case AAC_HWIF_STRONGARM: + AAC_DPRINTF(AAC_D_MISC, + ("set hardware up for StrongARM")); + sc->sc_if = aac_sa_interface; + break; + } + break; + } + + if (aac_attach(sc)) + goto bail_out; + + return; + + bail_out: + if (state > 1) + pci_intr_disestablish(pc, sc->sc_ih); + if (state > 0) + bus_space_unmap(sc->sc_memt, sc->sc_memh, memsize); + return; +} diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index 1c685c5d91f..fbe8aa61feb 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.85 2000/10/17 15:47:12 jason Exp $ +# $OpenBSD: files.pci,v 1.86 2000/11/10 09:39:36 niklas Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -105,6 +105,12 @@ file dev/pci/auvia.c auvia attach gdt at pci with gdt_pci file dev/pci/gdt_pci.c gdt_pci +# Adaptec FSA (file system accelerators) RAID adapters +device aac: scsi +attach aac at pci with aac_pci +file dev/pci/aac_pci.c aac_pci +file dev/ic/aac.c aac + # Qlogic ISP 10x0 (PCI) family # device declaration in sys/conf/files attach isp at pci with isp_pci |