diff options
| author | ad <ad@NetBSD.org> | 2001-07-30 19:59:04 +0000 |
|---|---|---|
| committer | ad <ad@NetBSD.org> | 2001-07-30 19:59:04 +0000 |
| commit | 3b1828355b3935a08fbdbf5c44ca48fbc92f4fa6 (patch) | |
| tree | 34dfa5a256ed746e20d1de1d28cf3d7774b5e5c1 /sys/dev | |
| parent | b0178f25e3e1cc9cdcb1820e07e7f63f4d366656 (diff) | |
Add a driver for Mylex AcceleRAID and eXtremeRAID controllers with v6
firmware. Based off the FreeBSD driver, and re-worked by tls, erh and I.
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/pci/Makefile | 4 | ||||
| -rw-r--r-- | sys/dev/pci/files.pci | 9 | ||||
| -rw-r--r-- | sys/dev/pci/mly.c | 2454 | ||||
| -rw-r--r-- | sys/dev/pci/mly_tables.h | 375 | ||||
| -rw-r--r-- | sys/dev/pci/mlyio.h | 111 | ||||
| -rw-r--r-- | sys/dev/pci/mlyreg.h | 1313 | ||||
| -rw-r--r-- | sys/dev/pci/mlyvar.h | 345 |
7 files changed, 4607 insertions, 4 deletions
diff --git a/sys/dev/pci/Makefile b/sys/dev/pci/Makefile index d9bf996b393..37a8b90a728 100644 --- a/sys/dev/pci/Makefile +++ b/sys/dev/pci/Makefile @@ -1,10 +1,10 @@ -# $NetBSD: Makefile,v 1.7 2001/04/11 07:42:37 jdolecek Exp $ +# $NetBSD: Makefile,v 1.8 2001/07/30 19:59:06 ad Exp $ # use 'make -f Makefile.pcidevs' to make pcidevs.h and pcidevs_data.h INCSDIR= /usr/include/dev/pci # Only install includes which are used by userland -INCS= if_lmc_types.h if_lmcioctl.h tgareg.h +INCS= if_lmc_types.h if_lmcioctl.h mlyio.h mlyreg.h tgareg.h .include <bsd.kinc.mk> diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index 5fa8a6ee600..4c27986ff52 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $NetBSD: files.pci,v 1.133 2001/07/25 00:11:51 thorpej Exp $ +# $NetBSD: files.pci,v 1.134 2001/07/30 19:59:06 ad Exp $ # # Config file and device description for machine-independent PCI code. # Included by ports that need it. Requires that the SCSI files be @@ -54,10 +54,15 @@ file dev/pci/ld_twe.c ld_twe attach cac at pci with cac_pci file dev/pci/cac_pci.c cac_pci -# Mylex RAID controllers +# Mylex DAC960 RAID controllers attach mlx at pci with mlx_pci file dev/pci/mlx_pci.c mlx_pci +# Newer Mylex AcceleRAID and eXtremeRAID controllers +device mly: scsi +attach mly at pci +file dev/pci/mly.c mly + # DPT EATA SCSI controllers attach dpt at pci with dpt_pci file dev/pci/dpt_pci.c dpt_pci diff --git a/sys/dev/pci/mly.c b/sys/dev/pci/mly.c new file mode 100644 index 00000000000..c7cd22055cb --- /dev/null +++ b/sys/dev/pci/mly.c @@ -0,0 +1,2454 @@ +/* $NetBSD: mly.c,v 1.1 2001/07/30 19:59:06 ad Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Andrew Doran, Thor Lancelot Simon, and Eric Haszlakiewicz. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/*- + * Copyright (c) 2000, 2001 Michael Smith + * Copyright (c) 2000 BSDi + * 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. + * + * from FreeBSD: mly.c,v 1.8 2001/07/14 00:12:22 msmith Exp + */ + +/* + * Driver for the Mylex AcceleRAID and eXtremeRAID family with v6 firmware. + * + * TODO: + * + * o Make mly->mly_btl a hash, then MLY_BTL_RESCAN becomes a SIMPLEQ. + * o Handle FC and multiple LUNs. + * o Fix mmbox usage. + * o Fix transfer speed fudge. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/queue.h> +#include <sys/buf.h> +#include <sys/endian.h> +#include <sys/conf.h> +#include <sys/malloc.h> +#include <sys/ioctl.h> +#include <sys/scsiio.h> +#include <sys/kthread.h> + +#include <uvm/uvm_extern.h> + +#include <machine/bus.h> + +#include <dev/scsipi/scsi_all.h> +#include <dev/scsipi/scsipi_all.h> +#include <dev/scsipi/scsiconf.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> + +#include <dev/pci/mlyreg.h> +#include <dev/pci/mlyio.h> +#include <dev/pci/mlyvar.h> +#include <dev/pci/mly_tables.h> + +static void mly_attach(struct device *, struct device *, void *); +static int mly_match(struct device *, struct cfdata *, void *); +static const struct mly_ident *mly_find_ident(struct pci_attach_args *); +static int mly_fwhandshake(struct mly_softc *); +static int mly_flush(struct mly_softc *); +static int mly_intr(void *); +static void mly_shutdown(void *); + +static int mly_alloc_ccbs(struct mly_softc *); +static void mly_check_event(struct mly_softc *); +static void mly_complete_event(struct mly_softc *, struct mly_ccb *); +static void mly_complete_rescan(struct mly_softc *, struct mly_ccb *); +static int mly_dmamem_alloc(struct mly_softc *, int, bus_dmamap_t *, + caddr_t *, bus_addr_t *, bus_dma_segment_t *); +static void mly_dmamem_free(struct mly_softc *, int, bus_dmamap_t, + caddr_t, bus_dma_segment_t *); +static int mly_enable_mmbox(struct mly_softc *); +static void mly_fetch_event(struct mly_softc *); +static int mly_get_controllerinfo(struct mly_softc *); +static int mly_get_eventstatus(struct mly_softc *); +static int mly_ioctl(struct mly_softc *, struct mly_cmd_ioctl *, + void **, size_t, void *, size_t *); +static void mly_padstr(char *, const char *, int); +static void mly_process_event(struct mly_softc *, struct mly_event *); +static void mly_release_ccbs(struct mly_softc *); +static int mly_scan_btl(struct mly_softc *, int, int); +static void mly_scan_channel(struct mly_softc *, int); +static void mly_thread(void *); +static void mly_thread_create(void *); + +static int mly_ccb_alloc(struct mly_softc *, struct mly_ccb **); +static void mly_ccb_complete(struct mly_softc *, struct mly_ccb *); +static void mly_ccb_enqueue(struct mly_softc *, struct mly_ccb *); +static void mly_ccb_free(struct mly_softc *, struct mly_ccb *); +static int mly_ccb_map(struct mly_softc *, struct mly_ccb *); +static int mly_ccb_poll(struct mly_softc *, struct mly_ccb *, int); +static int mly_ccb_submit(struct mly_softc *, struct mly_ccb *); +static void mly_ccb_unmap(struct mly_softc *, struct mly_ccb *); +static int mly_ccb_wait(struct mly_softc *, struct mly_ccb *, int); + +static void mly_get_xfer_mode(struct mly_softc *, int, + struct scsipi_xfer_mode *); +static void mly_scsipi_complete(struct mly_softc *, struct mly_ccb *); +static int mly_scsipi_ioctl(struct scsipi_channel *, u_long, caddr_t, + int, struct proc *); +static void mly_scsipi_minphys(struct buf *); +static void mly_scsipi_request(struct scsipi_channel *, + scsipi_adapter_req_t, void *); + +static int mly_user_command(struct mly_softc *, struct mly_user_command *); +static int mly_user_health(struct mly_softc *, struct mly_user_health *); + +cdev_decl(mly); + +extern struct cfdriver mly_cd; + +struct cfattach mly_ca = { + sizeof(struct mly_softc), mly_match, mly_attach +}; + +struct mly_ident { + u_short vendor; + u_short product; + u_short subvendor; + u_short subproduct; + int hwif; + const char *desc; +} static const mly_ident[] = { + { + PCI_VENDOR_MYLEX, + PCI_PRODUCT_MYLEX_EXTREMERAID, + PCI_VENDOR_MYLEX, + 0x0040, + MLY_HWIF_STRONGARM, + "eXtremeRAID 2000" + }, + { + PCI_VENDOR_MYLEX, + PCI_PRODUCT_MYLEX_EXTREMERAID, + PCI_VENDOR_MYLEX, + 0x0030, + MLY_HWIF_STRONGARM, + "eXtremeRAID 3000" + }, + { + PCI_VENDOR_MYLEX, + PCI_PRODUCT_MYLEX_ACCELERAID, + PCI_VENDOR_MYLEX, + 0x0050, + MLY_HWIF_I960RX, + "AcceleRAID 352" + }, + { + PCI_VENDOR_MYLEX, + PCI_PRODUCT_MYLEX_ACCELERAID, + PCI_VENDOR_MYLEX, + 0x0052, + MLY_HWIF_I960RX, + "AcceleRAID 170" + }, + { + PCI_VENDOR_MYLEX, + PCI_PRODUCT_MYLEX_ACCELERAID, + PCI_VENDOR_MYLEX, + 0x0054, + MLY_HWIF_I960RX, + "AcceleRAID 160" + }, +}; + +static void *mly_sdh; + +/* + * Try to find a `mly_ident' entry corresponding to this board. + */ +static const struct mly_ident * +mly_find_ident(struct pci_attach_args *pa) +{ + const struct mly_ident *mpi, *maxmpi; + pcireg_t reg; + + mpi = mly_ident; + maxmpi = mpi + sizeof(mly_ident) / sizeof(mly_ident[0]); + + for (; mpi < maxmpi; mpi++) { + if (PCI_VENDOR(pa->pa_id) != mpi->vendor || + PCI_PRODUCT(pa->pa_id) != mpi->product) + continue; + + if (mpi->subvendor == 0x0000) + return (mpi); + + reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG); + + if (PCI_VENDOR(reg) == mpi->subvendor && + PCI_PRODUCT(reg) == mpi->subproduct) + return (mpi); + } + + return (NULL); +} + +/* + * Match a supported board. + */ +static int +mly_match(struct device *parent, struct cfdata *cfdata, void *aux) +{ + + return (mly_find_ident(aux) != NULL); +} + +/* + * Attach a supported board. + */ +static void +mly_attach(struct device *parent, struct device *self, void *aux) +{ + struct pci_attach_args *pa; + struct mly_softc *mly; + struct mly_ioctl_getcontrollerinfo *mi; + const struct mly_ident *ident; + pci_chipset_tag_t pc; + pci_intr_handle_t ih; + bus_space_handle_t memh, ioh; + bus_space_tag_t memt, iot; + pcireg_t reg; + const char *intrstr; + int ior, memr, i, rv, state; + struct scsipi_adapter *adapt; + struct scsipi_channel *chan; + + mly = (struct mly_softc *)self; + pa = aux; + pc = pa->pa_pc; + ident = mly_find_ident(pa); + state = 0; + + mly->mly_dmat = pa->pa_dmat; + mly->mly_hwif = ident->hwif; + + printf(": Mylex %s\n", ident->desc); + + /* + * Map the PCI register window. + */ + memr = -1; + ior = -1; + + for (i = 0x10; i <= 0x14; i += 4) { + reg = pci_conf_read(pa->pa_pc, pa->pa_tag, i); + + if (PCI_MAPREG_TYPE(reg) == PCI_MAPREG_TYPE_IO) { + if (ior == -1 && PCI_MAPREG_IO_SIZE(reg) != 0) + ior = i; + } else { + if (memr == -1 && PCI_MAPREG_MEM_SIZE(reg) != 0) + memr = i; + } + } + + if (memr != -1) + if (pci_mapreg_map(pa, memr, PCI_MAPREG_TYPE_MEM, 0, + &memt, &memh, NULL, NULL)) + memr = -1; + if (ior != -1) + if (pci_mapreg_map(pa, ior, PCI_MAPREG_TYPE_IO, 0, + &iot, &ioh, NULL, NULL)) + ior = -1; + + if (memr != -1) { + mly->mly_iot = memt; + mly->mly_ioh = memh; + } else if (ior != -1) { + mly->mly_iot = iot; + mly->mly_ioh = ioh; + } else { + printf("%s: can't map i/o or memory space\n", self->dv_xname); + return; + } + + /* + * Enable the device. + */ + reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, + reg | PCI_COMMAND_MASTER_ENABLE); + + /* + * Map and establish the interrupt. + */ + if (pci_intr_map(pa, &ih)) { + printf("%s: can't map interrupt\n", self->dv_xname); + return; + } + intrstr = pci_intr_string(pc, ih); + mly->mly_ih = pci_intr_establish(pc, ih, IPL_BIO, mly_intr, mly); + if (mly->mly_ih == NULL) { + printf("%s: can't establish interrupt", self->dv_xname); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + + if (intrstr != NULL) + printf("%s: interrupting at %s\n", mly->mly_dv.dv_xname, + intrstr); + + /* + * Take care of interface-specific tasks. + */ + switch (mly->mly_hwif) { + case MLY_HWIF_I960RX: + mly->mly_doorbell_true = 0x00; + mly->mly_cmd_mailbox = MLY_I960RX_COMMAND_MAILBOX; + mly->mly_status_mailbox = MLY_I960RX_STATUS_MAILBOX; + mly->mly_idbr = MLY_I960RX_IDBR; + mly->mly_odbr = MLY_I960RX_ODBR; + mly->mly_error_status = MLY_I960RX_ERROR_STATUS; + mly->mly_interrupt_status = MLY_I960RX_INTERRUPT_STATUS; + mly->mly_interrupt_mask = MLY_I960RX_INTERRUPT_MASK; + break; + + case MLY_HWIF_STRONGARM: + mly->mly_doorbell_true = 0xff; + mly->mly_cmd_mailbox = MLY_STRONGARM_COMMAND_MAILBOX; + mly->mly_status_mailbox = MLY_STRONGARM_STATUS_MAILBOX; + mly->mly_idbr = MLY_STRONGARM_IDBR; + mly->mly_odbr = MLY_STRONGARM_ODBR; + mly->mly_error_status = MLY_STRONGARM_ERROR_STATUS; + mly->mly_interrupt_status = MLY_STRONGARM_INTERRUPT_STATUS; + mly->mly_interrupt_mask = MLY_STRONGARM_INTERRUPT_MASK; + break; + } + + /* + * Allocate and map the scatter/gather lists. + */ + rv = mly_dmamem_alloc(mly, MLY_SGL_SIZE * MLY_MAX_CCBS, + &mly->mly_sg_dmamap, (caddr_t *)&mly->mly_sg, + &mly->mly_sg_busaddr, &mly->mly_sg_seg); + if (rv) { + printf("%s: unable to allocate S/G maps\n", + mly->mly_dv.dv_xname); + goto bad; + } + state++; + + /* + * Allocate and map the memory mailbox. + */ + rv = mly_dmamem_alloc(mly, sizeof(struct mly_mmbox), + &mly->mly_mmbox_dmamap, (caddr_t *)&mly->mly_mmbox, + &mly->mly_mmbox_busaddr, &mly->mly_mmbox_seg); + if (rv) { + printf("%s: unable to allocate mailboxes\n", + mly->mly_dv.dv_xname); + goto bad; + } + state++; + + /* + * Initialise per-controller queues. + */ + SLIST_INIT(&mly->mly_ccb_free); + SIMPLEQ_INIT(&mly->mly_ccb_queue); + + /* + * Disable interrupts before we start talking to the controller. + */ + mly_outb(mly, mly->mly_interrupt_mask, MLY_INTERRUPT_MASK_DISABLE); + + /* + * Wait for the controller to come ready, handshaking with the + * firmware if required. This is typically only necessary on + * platforms where the controller BIOS does not run. + */ + if (mly_fwhandshake(mly)) { + printf("%s: unable to bring controller online\n", + mly->mly_dv.dv_xname); + goto bad; + } + + /* + * Allocate initial command buffers, obtain controller feature + * information, and then reallocate command buffers, since we'll + * know how many we want. + */ + if (mly_alloc_ccbs(mly)) { + printf("%s: unable to allocate CCBs\n", + mly->mly_dv.dv_xname); + goto bad; + } + state++; + if (mly_get_controllerinfo(mly)) { + printf("%s: unable to retrieve controller info\n", + mly->mly_dv.dv_xname); + goto bad; + } + mly_release_ccbs(mly); + if (mly_alloc_ccbs(mly)) { + printf("%s: unable to allocate CCBs\n", + mly->mly_dv.dv_xname); + state--; + goto bad; + } + + /* + * Get the current event counter for health purposes, populate the + * initial health status buffer. + */ + if (mly_get_eventstatus(mly)) { + printf("%s: unable to retrieve event status\n", + mly->mly_dv.dv_xname); + goto bad; + } + + /* + * Enable memory-mailbox mode. + */ + if (mly_enable_mmbox(mly)) { + printf("%s: unable to enable memory mailbox\n", + mly->mly_dv.dv_xname); + goto bad; + } + + /* + * Print a little information about the controller. + */ + mi = mly->mly_controllerinfo; + + printf("%s: %d physical channel%s, firmware %d.%02d-%d-%02d " + "(%02d%02d%02d%02d), %dMB RAM\n", mly->mly_dv.dv_xname, + mi->physical_channels_present, + (mi->physical_channels_present) > 1 ? "s" : "", + mi->fw_major, mi->fw_minor, mi->fw_turn, mi->fw_build, + mi->fw_century, mi->fw_year, mi->fw_month, mi->fw_day, + le16toh(mi->memory_size)); + + /* + * Register our `shutdownhook'. + */ + if (mly_sdh == NULL) + shutdownhook_establish(mly_shutdown, NULL); + + /* + * Clear any previous BTL information. For each bus that scsipi + * wants to scan, we'll receive the SCBUSIOLLSCAN ioctl and retrieve + * all BTL info at that point. + */ + memset(&mly->mly_btl, 0, sizeof(mly->mly_btl)); + + mly->mly_nchans = mly->mly_controllerinfo->physical_channels_present + + mly->mly_controllerinfo->virtual_channels_present; + + /* + * Attach to scsipi. + */ + adapt = &mly->mly_adapt; + memset(adapt, 0, sizeof(*adapt)); + adapt->adapt_dev = &mly->mly_dv; + adapt->adapt_nchannels = mly->mly_nchans; + adapt->adapt_openings = mly->mly_ncmds - MLY_CCBS_RESV; + adapt->adapt_max_periph = mly->mly_ncmds - MLY_CCBS_RESV; + adapt->adapt_request = mly_scsipi_request; + adapt->adapt_minphys = mly_scsipi_minphys; + adapt->adapt_ioctl = mly_scsipi_ioctl; + + for (i = 0; i < mly->mly_nchans; i++) { + chan = &mly->mly_chans[i]; + memset(chan, 0, sizeof(*chan)); + chan->chan_adapter = adapt; + chan->chan_bustype = &scsi_bustype; + chan->chan_channel = i; + chan->chan_ntargets = MLY_MAX_TARGETS; + chan->chan_nluns = MLY_MAX_LUNS; + chan->chan_id = mly->mly_controllerparam->initiator_id; + chan->chan_flags = SCSIPI_CHAN_NOSETTLE; + config_found(&mly->mly_dv, chan, scsiprint); + } + + /* + * Now enable interrupts... + */ + mly_outb(mly, mly->mly_interrupt_mask, MLY_INTERRUPT_MASK_ENABLE); + + /* + * Finally, create our monitoring thread. + */ + kthread_create(mly_thread_create, mly); + + mly->mly_state |= MLY_STATE_INITOK; + return; + + bad: + if (state > 2) + mly_release_ccbs(mly); + if (state > 1) + mly_dmamem_free(mly, sizeof(struct mly_mmbox), + mly->mly_mmbox_dmamap, (caddr_t)mly->mly_mmbox, + &mly->mly_mmbox_seg); + if (state > 0) + mly_dmamem_free(mly, MLY_SGL_SIZE * MLY_MAX_CCBS, + mly->mly_sg_dmamap, (caddr_t)mly->mly_sg, + &mly->mly_sg_seg); +} + +/* + * Scan all possible devices on the specified channel. + */ +static void +mly_scan_channel(struct mly_softc *mly, int bus) +{ + int startbus, endbus, s, target; + + if (bus == -1) { + startbus = 0; + endbus = mly->mly_nchans - 1; + } else { + startbus = bus; + endbus = bus; + } + + for (bus = startbus; bus <= endbus; bus++) + for (target = 0; target < MLY_MAX_TARGETS; target++) { + s = splbio(); + if (!mly_scan_btl(mly, bus, target)) { + tsleep(&mly->mly_btl[bus][target], PRIBIO, + "mlyscan", 0); + } + splx(s); + } +} + +/* + * Shut down all configured `mly' devices. + */ +static void +mly_shutdown(void *cookie) +{ + struct mly_softc *mly; + int i; + + for (i = 0; i < mly_cd.cd_ndevs; i++) { + if ((mly = device_lookup(&mly_cd, i)) == NULL) + continue; + + if (mly_flush(mly)) + printf("%s: unable to flush cache\n", + mly->mly_dv.dv_xname); + } +} + +/* + * Fill in the mly_controllerinfo and mly_controllerparam fields in the + * softc. + */ +static int +mly_get_controllerinfo(struct mly_softc *mly) +{ + struct mly_cmd_ioctl mci; + int rv; + + /* + * Build the getcontrollerinfo ioctl and send it. + */ + memset(&mci, 0, sizeof(mci)); + mci.sub_ioctl = MDACIOCTL_GETCONTROLLERINFO; + rv = mly_ioctl(mly, &mci, (void **)&mly->mly_controllerinfo, + sizeof(*mly->mly_controllerinfo), NULL, NULL); + if (rv != 0) + return (rv); + + /* + * Build the getcontrollerparameter ioctl and send it. + */ + memset(&mci, 0, sizeof(mci)); + mci.sub_ioctl = MDACIOCTL_GETCONTROLLERPARAMETER; + rv = mly_ioctl(mly, &mci, (void **)&mly->mly_controllerparam, + sizeof(*mly->mly_controllerparam), NULL, NULL); + + return (rv); +} + +/* + * Rescan a device, possibly as a consequence of getting an event which + * suggests that it may have changed. Must be called with interrupts + * blocked. + */ +static int +mly_scan_btl(struct mly_softc *mly, int bus, int target) +{ + struct mly_ccb *mc; + struct mly_cmd_ioctl *mci; + int rv; + + if (target == mly->mly_controllerparam->initiator_id) { + mly->mly_btl[bus][target].mb_flags = MLY_BTL_PROTECTED; + return (EIO); + } + + /* Don't re-scan if a scan is already in progress. */ + if ((mly->mly_btl[bus][target].mb_flags & MLY_BTL_SCANNING) != 0) + return (EBUSY); + + /* Get a command. */ + if ((rv = mly_ccb_alloc(mly, &mc)) != 0) + return (rv); + + /* Set up the data buffer. */ + mc->mc_data = malloc(sizeof(union mly_devinfo), + M_DEVBUF, M_NOWAIT); + memset(mc->mc_data, 0, sizeof(union mly_devinfo)); + + mc->mc_flags |= MLY_CCB_DATAIN; + mc->mc_complete = mly_complete_rescan; + + /* + * Build the ioctl. + */ + mci = (struct mly_cmd_ioctl *)&mc->mc_packet->ioctl; + mci->opcode = MDACMD_IOCTL; + mci->timeout = 30 | MLY_TIMEOUT_SECONDS; + memset(&mci->param, 0, sizeof(mci->param)); + + if (MLY_BUS_IS_VIRTUAL(mly, bus)) { + mc->mc_length = sizeof(struct mly_ioctl_getlogdevinfovalid); + mci->data_size = htole32(mc->mc_length); + mci->sub_ioctl = MDACIOCTL_GETLOGDEVINFOVALID; + _lto3l(MLY_LOGADDR(0, MLY_LOGDEV_ID(mly, bus, target)), + mci->addr); + } else { + mc->mc_length = sizeof(struct mly_ioctl_getphysdevinfovalid); + mci->data_size = htole32(mc->mc_length); + mci->sub_ioctl = MDACIOCTL_GETPHYSDEVINFOVALID; + _lto3l(MLY_PHYADDR(0, bus, target, 0), mci->addr); + } + + /* + * Dispatch the command. + */ + if ((rv = mly_ccb_map(mly, mc)) != 0) + goto bad; + mly->mly_btl[bus][target].mb_flags |= MLY_BTL_SCANNING; + mly_ccb_enqueue(mly, mc); + return (0); + + bad: + free(mc->mc_data, M_DEVBUF); + mly_ccb_free(mly, mc); + return(rv); +} + +/* + * Handle the completion of a rescan operation. + */ +static void +mly_complete_rescan(struct mly_softc *mly, struct mly_ccb *mc) +{ + struct mly_ioctl_getlogdevinfovalid *ldi; + struct mly_ioctl_getphysdevinfovalid *pdi; + struct mly_cmd_ioctl *mci; + struct mly_btl btl, *btlp; + struct scsipi_xfer_mode xm; + int bus, target, rescan; + u_int tmp; + + mly_ccb_unmap(mly, mc); + + /* + * Recover the bus and target from the command. We need these even + * in the case where we don't have a useful response. + */ + mci = (struct mly_cmd_ioctl *)&mc->mc_packet->ioctl; + tmp = _3ltol(mci->addr); + rescan = 0; + + if (mci->sub_ioctl == MDACIOCTL_GETLOGDEVINFOVALID) { + bus = MLY_LOGDEV_BUS(mly, MLY_LOGADDR_DEV(tmp)); + target = MLY_LOGDEV_TARGET(mly, MLY_LOGADDR_DEV(tmp)); + } else { + bus = MLY_PHYADDR_CHANNEL(tmp); + target = MLY_PHYADDR_TARGET(tmp); + } + + /* XXX Validate bus/target? */ + btlp = &mly->mly_btl[bus][target]; + + /* The default result is 'no device'. */ + memset(&btl, 0, sizeof(btl)); + btl.mb_flags = MLY_BTL_PROTECTED; + + /* If the rescan completed OK, we have possibly-new BTL data. */ + if (mc->mc_status != 0) + goto out; + + if (mc->mc_length == sizeof(*ldi)) { + ldi = (struct mly_ioctl_getlogdevinfovalid *)mc->mc_data; + tmp = le32toh(ldi->logical_device_number); + + if (MLY_LOGDEV_BUS(mly, tmp) != bus || + MLY_LOGDEV_TARGET(mly, tmp) != target) { +#ifdef MLYDEBUG_OBNOXIOUS + printf("%s: WARNING: BTL rescan (logical) for %d:%d " + "returned data for %d:%d instead\n", + mly->mly_dv.dv_xname, bus, target, + MLY_LOGDEV_BUS(mly, tmp), + MLY_LOGDEV_TARGET(mly, tmp)); +#endif + goto out; + } + + btl.mb_flags = MLY_BTL_LOGICAL | MLY_BTL_TQING; + btl.mb_type = ldi->raid_level; + btl.mb_state = ldi->state; + } else if (mc->mc_length == sizeof(*pdi)) { + pdi = (struct mly_ioctl_getphysdevinfovalid *)mc->mc_data; + + if (pdi->channel != bus || pdi->target != target) { +#ifdef MLYDEBUG_OBNOXIOUS + printf("%s: WARNING: BTL rescan (physical) for %d:%d " + " returned data for %d:%d instead\n", + mly->mly_dv.dv_xname, + bus, target, pdi->channel, pdi->target); +#endif + goto out; + } + + btl.mb_flags = MLY_BTL_PHYSICAL; + btl.mb_type = MLY_DEVICE_TYPE_PHYSICAL; + btl.mb_state = pdi->state; + btl.mb_speed = pdi->speed; + btl.mb_width = pdi->width; + + if (pdi->state != MLY_DEVICE_STATE_UNCONFIGURED) + btl.mb_flags |= MLY_BTL_PROTECTED; + if (pdi->command_tags != 0) + btl.mb_flags |= MLY_BTL_TQING; + } else { + printf("%s: BTL rescan result invalid\n", mly->mly_dv.dv_xname); + goto out; + } + + /* Decide whether we need to rescan the device. */ + if (btl.mb_flags != btlp->mb_flags || + btl.mb_speed != btlp->mb_speed || + btl.mb_width != btlp->mb_width) + rescan = 1; + + out: + *btlp = btl; + + if (rescan && (btl.mb_flags & MLY_BTL_PROTECTED) == 0) { + xm.xm_target = target; + mly_get_xfer_mode(mly, bus, &xm); + /* XXX SCSI mid-layer rescan goes here. */ + } + + /* Wake anybody waiting on the device to be rescanned. */ + wakeup(btlp); + + free(mc->mc_data, M_DEVBUF); + mly_ccb_free(mly, mc); +} + +/* + * Get the current health status and set the 'next event' counter to suit. + */ +static int +mly_get_eventstatus(struct mly_softc *mly) +{ + struct mly_cmd_ioctl mci; + struct mly_health_status *mh; + int rv; + + /* Build the gethealthstatus ioctl and send it. */ + memset(&mci, 0, sizeof(mci)); + mh = NULL; + mci.sub_ioctl = MDACIOCTL_GETHEALTHSTATUS; + + rv = mly_ioctl(mly, &mci, (void **)&mh, sizeof(*mh), NULL, NULL); + if (rv) + return (rv); + + /* Get the event counter. */ + mly->mly_event_change = le32toh(mh->change_counter); + mly->mly_event_waiting = le32toh(mh->next_event); + mly->mly_event_counter = le32toh(mh->next_event); + + /* Save the health status into the memory mailbox */ + memcpy(&mly->mly_mmbox->mmm_health.status, mh, sizeof(*mh)); + + bus_dmamap_sync(mly->mly_dmat, mly->mly_mmbox_dmamap, + offsetof(struct mly_mmbox, mmm_health), + sizeof(mly->mly_mmbox->mmm_health), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + + free(mh, M_DEVBUF); + return (0); +} + +/* + * Enable the memory mailbox mode. + */ +static int +mly_enable_mmbox(struct mly_softc *mly) +{ + struct mly_cmd_ioctl mci; + u_int8_t *sp; + u_int64_t tmp; + int rv; + + /* Build the ioctl and send it. */ + memset(&mci, 0, sizeof(mci)); + mci.sub_ioctl = MDACIOCTL_SETMEMORYMAILBOX; + + /* Set buffer addresses. */ + tmp = mly->mly_mmbox_busaddr + offsetof(struct mly_mmbox, mmm_command); + mci.param.setmemorymailbox.command_mailbox_physaddr = htole64(tmp); + + tmp = mly->mly_mmbox_busaddr + offsetof(struct mly_mmbox, mmm_status); + mci.param.setmemorymailbox.status_mailbox_physaddr = htole64(tmp); + + tmp = mly->mly_mmbox_busaddr + offsetof(struct mly_mmbox, mmm_health); + mci.param.setmemorymailbox.health_buffer_physaddr = htole64(tmp); + + /* Set buffer sizes - abuse of data_size field is revolting. */ + sp = (u_int8_t *)&mci.data_size; + sp[0] = (sizeof(union mly_cmd_packet) * MLY_MMBOX_COMMANDS) >> 10; + sp[1] = (sizeof(union mly_status_packet) * MLY_MMBOX_STATUS) >> 10; + mci.param.setmemorymailbox.health_buffer_size = + sizeof(union mly_health_region) >> 10; + + rv = mly_ioctl(mly, &mci, NULL, 0, NULL, NULL); + if (rv) + return (rv); + + mly->mly_state |= MLY_STATE_MMBOX_ACTIVE; + return (0); +} + +/* + * Flush all pending I/O from the controller. + */ +static int +mly_flush(struct mly_softc *mly) +{ + struct mly_cmd_ioctl mci; + + /* Build the ioctl */ + memset(&mci, 0, sizeof(mci)); + mci.sub_ioctl = MDACIOCTL_FLUSHDEVICEDATA; + mci.param.deviceoperation.operation_device = + MLY_OPDEVICE_PHYSICAL_CONTROLLER; + + /* Pass it off to the controller */ + return (mly_ioctl(mly, &mci, NULL, 0, NULL, NULL)); +} + +/* + * Perform an ioctl command. + * + * If (data) is not NULL, the command requires data transfer. If (*data) is + * NULL the command requires data transfer from the controller, and we will + * allocate a buffer for it. If (*data) is not NULL, the command requires + * data transfer to the controller. + */ +static int +mly_ioctl(struct mly_softc *mly, struct mly_cmd_ioctl *ioctl, void **data, + size_t datasize, void *sense_buffer, + size_t *sense_length) +{ + struct mly_ccb *mc; + struct mly_cmd_ioctl *mci; + u_int8_t status; + int rv; + + mc = NULL; + if ((rv = mly_ccb_alloc(mly, &mc)) != 0) + goto bad; + + /* + * Copy the ioctl structure, but save some important fields and then + * fixup. + */ + mci = &mc->mc_packet->ioctl; + ioctl->sense_buffer_address = htole64(mci->sense_buffer_address); + ioctl->maximum_sense_size = mci->maximum_sense_size; + *mci = *ioctl; + mci->opcode = MDACMD_IOCTL; + mci->timeout = 30 | MLY_TIMEOUT_SECONDS; + + /* Handle the data buffer. */ + if (data != NULL) { + if (*data == NULL) { + /* Allocate data buffer */ + mc->mc_data = malloc(datasize, M_DEVBUF, M_NOWAIT); + mc->mc_flags |= MLY_CCB_DATAIN; + } else { + mc->mc_data = *data; + mc->mc_flags |= MLY_CCB_DATAOUT; + } + mc->mc_length = datasize; + mc->mc_packet->generic.data_size = htole32(datasize); + } + + /* Run the command. */ + if (datasize > 0) + if ((rv = mly_ccb_map(mly, mc)) != 0) + goto bad; + rv = mly_ccb_poll(mly, mc, 30000); + if (datasize > 0) + mly_ccb_unmap(mly, mc); + if (rv != 0) + goto bad; + + /* Clean up and return any data. */ + status = mc->mc_status; + + if (status != 0) + printf("mly_ioctl: command status %d\n", status); + + if (mc->mc_sense > 0 && sense_buffer != NULL) { + memcpy(sense_buffer, mc->mc_packet, mc->mc_sense); + *sense_length = mc->mc_sense; + goto bad; + } + + /* Should we return a data pointer? */ + if (data != NULL && *data == NULL) + *data = mc->mc_data; + + /* Command completed OK. */ + rv = (status != 0 ? EIO : 0); + + bad: + if (mc != NULL) { + /* Do we need to free a data buffer we allocated? */ + if (rv != 0 && mc->mc_data != NULL && *data == NULL) + free(mc->mc_data, M_DEVBUF); + mly_ccb_free(mly, mc); + } + + return (rv); +} + +/* + * Check for event(s) outstanding in the controller. + */ +static void +mly_check_event(struct mly_softc *mly) +{ + + bus_dmamap_sync(mly->mly_dmat, mly->mly_mmbox_dmamap, + offsetof(struct mly_mmbox, mmm_health), + sizeof(mly->mly_mmbox->mmm_health), + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); + + /* + * The controller may have updated the health status information, so + * check for it here. Note that the counters are all in host + * memory, so this check is very cheap. Also note that we depend on + * checking on completion + */ + if (le32toh(mly->mly_mmbox->mmm_health.status.change_counter) != + mly->mly_event_change) { + mly->mly_event_change = + le32toh(mly->mly_mmbox->mmm_health.status.change_counter); + mly->mly_event_waiting = + le32toh(mly->mly_mmbox->mmm_health.status.next_event); + + /* Wake up anyone that might be interested in this. */ + wakeup(&mly->mly_event_change); + } + + bus_dmamap_sync(mly->mly_dmat, mly->mly_mmbox_dmamap, + offsetof(struct mly_mmbox, mmm_health), + sizeof(mly->mly_mmbox->mmm_health), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + + if (mly->mly_event_counter != mly->mly_event_waiting) + mly_fetch_event(mly); +} + +/* + * Fetch one event from the controller. If we fail due to resource + * starvation, we'll be retried the next time a command completes. + */ +static void +mly_fetch_event(struct mly_softc *mly) +{ + struct mly_ccb *mc; + struct mly_cmd_ioctl *mci; + int s; + u_int32_t event; + + /* Get a command. */ + if (mly_ccb_alloc(mly, &mc)) + return; + + /* Set up the data buffer. */ + mc->mc_data = malloc(sizeof(struct mly_event), M_DEVBUF, M_NOWAIT); + memset(mc->mc_data, 0, sizeof(struct mly_event)); + + mc->mc_length = sizeof(struct mly_event); + mc->mc_flags |= MLY_CCB_DATAIN; + mc->mc_complete = mly_complete_event; + + /* + * Get an event number to fetch. It's possible that we've raced + * with another context for the last event, in which case there will + * be no more events. + */ + s = splbio(); + if (mly->mly_event_counter == mly->mly_event_waiting) { + splx(s); + free(mc->mc_data, M_DEVBUF); + mly_ccb_free(mly, mc); + return; + } + event = mly->mly_event_counter++; + splx(s); + + /* + * Build the ioctl. + * + * At this point we are committed to sending this request, as it + * will be the only one constructed for this particular event + * number. + */ + mci = (struct mly_cmd_ioctl *)&mc->mc_packet->ioctl; + mci->opcode = MDACMD_IOCTL; + mci->data_size = htole32(sizeof(struct mly_event)); + _lto3l(MLY_PHYADDR(0, 0, (event >> 16) & 0xff, (event >> 24) & 0xff), + mci->addr); + mci->timeout = 30 | MLY_TIMEOUT_SECONDS; + mci->sub_ioctl = MDACIOCTL_GETEVENT; + mci->param.getevent.sequence_number_low = htole16(event & 0xffff); + + /* + * Submit the command. + */ + if (mly_ccb_map(mly, mc) != 0) + goto bad; + mly_ccb_enqueue(mly, mc); + return; + + bad: + printf("%s: couldn't fetch event %u\n", mly->mly_dv.dv_xname, event); + free(mc->mc_data, M_DEVBUF); + mly_ccb_free(mly, mc); +} + +/* + * Handle the completion of an event poll. + */ +static void +mly_complete_event(struct mly_softc *mly, struct mly_ccb *mc) +{ + struct mly_event *me; + + me = (struct mly_event *)mc->mc_data; + mly_ccb_unmap(mly, mc); + mly_ccb_free(mly, mc); + + /* If the event was successfully fetched, process it. */ + if (mc->mc_status == SCSI_OK) + mly_process_event(mly, me); + else + printf("%s: unable to fetch event; status = 0x%x\n", + mly->mly_dv.dv_xname, mc->mc_status); + + free(me, M_DEVBUF); + + /* Check for another event. */ + mly_check_event(mly); +} + +/* + * Process a controller event. Called with interupts blocked (i.e., at + * interrupt time). + */ +static void +mly_process_event(struct mly_softc *mly, struct mly_event *me) +{ + struct scsipi_sense_data *ssd; + int bus, target, event, class, action; + const char *fp, *tp; + + ssd = (struct scsipi_sense_data *)&me->sense[0]; + + /* + * Errors can be reported using vendor-unique sense data. In this + * case, the event code will be 0x1c (Request sense data present), + * the sense key will be 0x09 (vendor specific), the MSB of the ASC + * will be set, and the actual event code will be a 16-bit value + * comprised of the ASCQ (low byte) and low seven bits of the ASC + * (low seven bits of the high byte). + */ + if (le32toh(me->code) == 0x1c && + (ssd->flags & SSD_KEY) == SKEY_VENDOR_UNIQUE && + (ssd->add_sense_code & 0x80) != 0) { + event = ((int)(ssd->add_sense_code & ~0x80) << 8) + + ssd->add_sense_code_qual; + } else + event = le32toh(me->code); + + /* Look up event, get codes. */ + fp = mly_describe_code(mly_table_event, event); + + /* Quiet event? */ + class = fp[0]; +#ifdef notyet + if (isupper(class) && bootverbose) + class = tolower(class); +#endif + + /* Get action code, text string. */ + action = fp[1]; + tp = fp + 3; + + /* + * Print some information about the event. + * + * This code uses a table derived from the corresponding portion of + * the Linux driver, and thus the parser is very similar. + */ + switch (class) { + case 'p': + /* + * Error on physical drive. + */ + printf("%s: physical device %d:%d %s\n", mly->mly_dv.dv_xname, + me->channel, me->target, tp); + if (action == 'r') + mly->mly_btl[me->channel][me->target].mb_flags |= + MLY_BTL_RESCAN; + break; + + case 'l': + case 'm': + /* + * Error on logical unit, or message about logical unit. + * + * XXX Splitting me->lun as we do in the following can't possibly + * be correct. Where should we get this value? + */ + bus = MLY_LOGDEV_BUS(mly, me->lun); + target = MLY_LOGDEV_TARGET(mly, me->lun); + if (action == 'r') + mly->mly_btl[bus][target].mb_flags |= MLY_BTL_RESCAN; + break; + + case 's': + /* + * Report of sense data. + */ + if (((ssd->flags & SSD_KEY) == SKEY_NO_SENSE || + (ssd->flags & SSD_KEY) == SKEY_NOT_READY) && + ssd->add_sense_code == 0x04 && + (ssd->add_sense_code_qual == 0x01 || + ssd->add_sense_code_qual == 0x02)) { + /* Ignore NO_SENSE or NOT_READY in one case */ + break; + } + + /* + * XXX Should translate this if SCSIVERBOSE. + */ + printf("%s: physical device %d:%d %s\n", mly->mly_dv.dv_xname, + me->channel, me->target, tp); + printf("%s: sense key %d asc %02x ascq %02x\n", + mly->mly_dv.dv_xname, ssd->flags & SSD_KEY, + ssd->add_sense_code, ssd->add_sense_code_qual); + printf("%s: info %x%x%x%x csi %x%x%x%x\n", + mly->mly_dv.dv_xname, ssd->info[0], ssd->info[1], + ssd->info[2], ssd->info[3], ssd->cmd_spec_info[0], + ssd->cmd_spec_info[1], ssd->cmd_spec_info[2], + ssd->cmd_spec_info[3]); + if (action == 'r') + mly->mly_btl[me->channel][me->target].mb_flags |= + MLY_BTL_RESCAN; + break; + + case 'e': + printf("%s: ", mly->mly_dv.dv_xname); + printf(tp, me->target, me->lun); + break; + + case 'c': + printf("%s: controller %s\n", mly->mly_dv.dv_xname, tp); + break; + + case '?': + printf("%s: %s - %d\n", mly->mly_dv.dv_xname, tp, event); + break; + + default: + /* Probably a 'noisy' event being ignored. */ + break; + } +} + +/* + * Create the monitoring thread. Called after the standard kernel threads + * have been created. + */ +static void +mly_thread_create(void *cookie) +{ + struct mly_softc *mly; + int rv; + + mly = cookie; + + rv = kthread_create1(mly_thread, mly, &mly->mly_thread, "%s", + mly->mly_dv.dv_xname); + if (rv != 0) + printf("%s: unable to create thread (%d)\n", + mly->mly_dv.dv_xname, rv); +} + +/* + * Perform periodic activities. + */ +static void +mly_thread(void *cookie) +{ + struct mly_softc *mly; + struct mly_btl *btl; + int s, bus, target, done; + + mly = (struct mly_softc *)cookie; + + for (;;) { + /* Check for new events. */ + mly_check_event(mly); + + /* Re-scan up to 1 device. */ + s = splbio(); + for (bus = 0; bus < mly->mly_nchans && !done; bus++) { + for (target = 0; target < MLY_MAX_TARGETS; target++) { + /* Perform device rescan? */ + btl = &mly->mly_btl[bus][target]; + if ((btl->mb_flags & MLY_BTL_RESCAN) != 0) { + btl->mb_flags ^= MLY_BTL_RESCAN; + mly_scan_btl(mly, bus, target); + done = 1; + break; + } + } + } + splx(s); + + /* Sleep for N seconds. */ + tsleep(mly_thread, PWAIT, "mlyzzz", + hz * MLY_PERIODIC_INTERVAL); + } +} + +/* + * Submit a command to the controller and poll on completion. Return + * non-zero on timeout. + */ +static int +mly_ccb_poll(struct mly_softc *mly, struct mly_ccb *mc, int timo) +{ + int rv; + + if ((rv = mly_ccb_submit(mly, mc)) != 0) + return (rv); + + for (timo *= 10; timo != 0; timo--) { + if ((mc->mc_flags & MLY_CCB_COMPLETE) != 0) + break; + mly_intr(mly); + DELAY(100); + } + + return (timo == 0); +} + +/* + * Submit a command to the controller and sleep on completion. Return + * non-zero on timeout. + */ +static int +mly_ccb_wait(struct mly_softc *mly, struct mly_ccb *mc, int timo) +{ + int rv, s; + + mly_ccb_enqueue(mly, mc); + + s = splbio(); + if ((mc->mc_flags & MLY_CCB_COMPLETE) != 0) { + splx(s); + return (0); + } + rv = tsleep(mc, PRIBIO, "mlywccb", timo * hz / 1000); + splx(s); + + return (rv); +} + +/* + * If a CCB is specified, enqueue it. Pull CCBs off the software queue in + * the order that they were enqueued and try to submit their command blocks + * to the controller for execution. + */ +void +mly_ccb_enqueue(struct mly_softc *mly, struct mly_ccb *mc) +{ + int s; + + s = splbio(); + + if (mc != NULL) + SIMPLEQ_INSERT_TAIL(&mly->mly_ccb_queue, mc, mc_link.simpleq); + + while ((mc = SIMPLEQ_FIRST(&mly->mly_ccb_queue)) != NULL) { + if (mly_ccb_submit(mly, mc)) + break; + SIMPLEQ_REMOVE_HEAD(&mly->mly_ccb_queue, mc, mc_link.simpleq); + } + + splx(s); +} + +/* + * Deliver a command to the controller. + */ +static int +mly_ccb_submit(struct mly_softc *mly, struct mly_ccb *mc) +{ + union mly_cmd_packet *pkt; + int s, off; + + mc->mc_packet->generic.command_id = htole16(mc->mc_slot); + + bus_dmamap_sync(mly->mly_dmat, mly->mly_pkt_dmamap, + mc->mc_packetphys - mly->mly_pkt_busaddr, + sizeof(union mly_cmd_packet), + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + s = splbio(); + + /* + * Do we have to use the hardware mailbox? + */ + if ((mly->mly_state & MLY_STATE_MMBOX_ACTIVE) == 0) { + /* + * Check to see if the controller is ready for us. + */ + if (mly_idbr_true(mly, MLY_HM_CMDSENT)) { + splx(s); + return (EBUSY); + } + + /* + * It's ready, send the command. + */ + mly_outl(mly, mly->mly_cmd_mailbox, + (u_int64_t)mc->mc_packetphys & 0xffffffff); + mly_outl(mly, mly->mly_cmd_mailbox + 4, + (u_int64_t)mc->mc_packetphys >> 32); + mly_outb(mly, mly->mly_idbr, MLY_HM_CMDSENT); + } else { + pkt = &mly->mly_mmbox->mmm_command[mly->mly_mmbox_cmd_idx]; + off = (caddr_t)pkt - (caddr_t)mly->mly_mmbox; + + bus_dmamap_sync(mly->mly_dmat, mly->mly_mmbox_dmamap, + off, sizeof(mly->mly_mmbox->mmm_command[0]), + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); + + /* Check to see if the next index is free yet. */ + if (pkt->mmbox.flag != 0) { + splx(s); + return (EBUSY); + } + + /* Copy in new command */ + memcpy(pkt->mmbox.data, mc->mc_packet->mmbox.data, + sizeof(pkt->mmbox.data)); + + /* Copy flag last. */ + pkt->mmbox.flag = mc->mc_packet->mmbox.flag; + + bus_dmamap_sync(mly->mly_dmat, mly->mly_mmbox_dmamap, + off, sizeof(mly->mly_mmbox->mmm_command[0]), + BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); + + /* Signal controller and update index. */ + mly_outb(mly, mly->mly_idbr, MLY_AM_CMDSENT); + mly->mly_mmbox_cmd_idx = + (mly->mly_mmbox_cmd_idx + 1) % MLY_MMBOX_COMMANDS; + } + + splx(s); + return (0); +} + +/* + * Pick up completed commands from the controller and handle accordingly. + */ +int +mly_intr(void *cookie) +{ + struct mly_ccb *mc; + union mly_status_packet *sp; + u_int16_t slot; + int forus, off; + struct mly_softc *mly; + + mly = cookie; + forus = 0; + + /* + * Pick up hardware-mailbox commands. + */ + if (mly_odbr_true(mly, MLY_HM_STSREADY)) { + slot = mly_inw(mly, mly->mly_status_mailbox); + + if (slot < MLY_SLOT_MAX) { + mc = mly->mly_ccbs + (slot - MLY_SLOT_START); + mc->mc_status = + mly_inb(mly, mly->mly_status_mailbox + 2); + mc->mc_sense = + mly_inb(mly, mly->mly_status_mailbox + 3); + mc->mc_resid = + mly_inl(mly, mly->mly_status_mailbox + 4); + + mly_ccb_complete(mly, mc); + } else { + /* Slot 0xffff may mean "extremely bogus command". */ + printf("%s: got HM completion for illegal slot %u\n", + mly->mly_dv.dv_xname, slot); + } + + /* Unconditionally acknowledge status. */ + mly_outb(mly, mly->mly_odbr, MLY_HM_STSREADY); + mly_outb(mly, mly->mly_idbr, MLY_HM_STSACK); + forus = 1; + } + + /* + * Pick up memory-mailbox commands. + */ + if (mly_odbr_true(mly, MLY_AM_STSREADY)) { + for (;;) { + sp = &mly->mly_mmbox->mmm_status[mly->mly_mmbox_sts_idx]; + off = (caddr_t)sp - (caddr_t)mly->mly_mmbox; + + bus_dmamap_sync(mly->mly_dmat, mly->mly_mmbox_dmamap, + off, sizeof(mly->mly_mmbox->mmm_command[0]), + BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); + + /* Check for more status. */ + if (sp->mmbox.flag == 0) + break; + + /* Get slot number. */ + slot = le16toh(sp->status.command_id); + if (slot < MLY_SLOT_MAX) { + mc = mly->mly_ccbs + (slot - MLY_SLOT_START); + mc->mc_status = sp->status.status; + mc->mc_sense = sp->status.sense_length; + mc->mc_resid = le32toh(sp->status.residue); + mly_ccb_complete(mly, mc); + } else { + /* + * Slot 0xffff may mean "extremely bogus + * command". + */ + printf("%s: got AM completion for illegal " + "slot %u at %d\n", mly->mly_dv.dv_xname, + slot, mly->mly_mmbox_sts_idx); + } + + /* Clear and move to next index. */ + sp->mmbox.flag = 0; + mly->mly_mmbox_sts_idx = + (mly->mly_mmbox_sts_idx + 1) % MLY_MMBOX_STATUS; + } + + /* Acknowledge that we have collected status value(s). */ + mly_outb(mly, mly->mly_odbr, MLY_AM_STSREADY); + forus = 1; + } + + /* + * Run the queue. + */ + if (forus && SIMPLEQ_FIRST(&mly->mly_ccb_queue) != NULL) + mly_ccb_enqueue(mly, NULL); + + return (forus); +} + +/* + * Process completed commands + */ +static void +mly_ccb_complete(struct mly_softc *mly, struct mly_ccb *mc) +{ + void (*complete)(struct mly_softc *, struct mly_ccb *); + + bus_dmamap_sync(mly->mly_dmat, mly->mly_pkt_dmamap, + mc->mc_packetphys - mly->mly_pkt_busaddr, + sizeof(union mly_cmd_packet), + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + complete = mc->mc_complete; + mc->mc_flags |= MLY_CCB_COMPLETE; + + /* + * Call completion handler or wake up sleeping consumer. + */ + if (complete != NULL) + (*complete)(mly, mc); + else + wakeup(mc); +} + +/* + * Allocate a command. + */ +int +mly_ccb_alloc(struct mly_softc *mly, struct mly_ccb **mcp) +{ + struct mly_ccb *mc; + int s; + + s = splbio(); + mc = SLIST_FIRST(&mly->mly_ccb_free); + if (mc != NULL) + SLIST_REMOVE_HEAD(&mly->mly_ccb_free, mc_link.slist); + splx(s); + + *mcp = mc; + return (mc == NULL ? EAGAIN : 0); +} + +/* + * Release a command back to the freelist. + */ +void +mly_ccb_free(struct mly_softc *mly, struct mly_ccb *mc) +{ + int s; + + /* + * Fill in parts of the command that may cause confusion if a + * consumer doesn't when we are later allocated. + */ + mc->mc_data = NULL; + mc->mc_flags = 0; + mc->mc_complete = NULL; + mc->mc_private = NULL; + + /* + * By default, we set up to overwrite the command packet with sense + * information. + */ + mc->mc_packet->generic.command_control = 0; + mc->mc_packet->generic.sense_buffer_address = + htole64(mc->mc_packetphys); + mc->mc_packet->generic.maximum_sense_size = + sizeof(union mly_cmd_packet); + + s = splbio(); + SLIST_INSERT_HEAD(&mly->mly_ccb_free, mc, mc_link.slist); + splx(s); +} + +/* + * Allocate and initialise command and packet structures. + * + * If the controller supports fewer than MLY_MAX_CCBS commands, limit our + * allocation to that number. If we don't yet know how many commands the + * controller supports, allocate a very small set (suitable for initialisation + * purposes only). + */ +static int +mly_alloc_ccbs(struct mly_softc *mly) +{ + struct mly_ccb *mc; + int i, rv; + + if (mly->mly_controllerinfo == NULL) + mly->mly_ncmds = MLY_CCBS_RESV; + else { + i = le16toh(mly->mly_controllerinfo->maximum_parallel_commands); + mly->mly_ncmds = min(MLY_MAX_CCBS, i); + } + + /* + * Allocate enough space for all the command packets in one chunk + * and map them permanently into controller-visible space. + */ + rv = mly_dmamem_alloc(mly, + mly->mly_ncmds * sizeof(union mly_cmd_packet), + &mly->mly_pkt_dmamap, (caddr_t *)&mly->mly_pkt, + &mly->mly_pkt_busaddr, &mly->mly_pkt_seg); + if (rv) + return (rv); + + mly->mly_ccbs = malloc(sizeof(struct mly_ccb) * mly->mly_ncmds, + M_DEVBUF, M_NOWAIT); + memset(mly->mly_ccbs, 0, sizeof(struct mly_ccb) * mly->mly_ncmds); + + for (i = 0; i < mly->mly_ncmds; i++) { + mc = mly->mly_ccbs + i; + mc->mc_slot = MLY_SLOT_START + i; + mc->mc_packet = mly->mly_pkt + i; + mc->mc_packetphys = mly->mly_pkt_busaddr + + (i * sizeof(union mly_cmd_packet)); + + rv = bus_dmamap_create(mly->mly_dmat, MLY_MAX_XFER, + MLY_MAX_SEGS, MLY_MAX_XFER, 0, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, + &mc->mc_datamap); + if (rv) { + mly_release_ccbs(mly); + return (rv); + } + + mly_ccb_free(mly, mc); + } + + return (0); +} + +/* + * Free all the storage held by commands. + * + * Must be called with all commands on the free list. + */ +static void +mly_release_ccbs(struct mly_softc *mly) +{ + struct mly_ccb *mc; + + /* Throw away command buffer DMA maps. */ + while (mly_ccb_alloc(mly, &mc) == 0) + bus_dmamap_destroy(mly->mly_dmat, mc->mc_datamap); + + /* Release CCB storage. */ + free(mly->mly_ccbs, M_DEVBUF); + + /* Release the packet storage. */ + mly_dmamem_free(mly, mly->mly_ncmds * sizeof(union mly_cmd_packet), + mly->mly_pkt_dmamap, (caddr_t)mly->mly_pkt, &mly->mly_pkt_seg); +} + +/* + * Map a command into controller-visible space. + */ +static int +mly_ccb_map(struct mly_softc *mly, struct mly_ccb *mc) +{ + struct mly_cmd_generic *gen; + struct mly_sg_entry *sg; + bus_dma_segment_t *ds; + int flg, nseg, rv; + +#ifdef DIAGNOSTIC + /* Don't map more than once. */ + if ((mc->mc_flags & MLY_CCB_MAPPED) != 0) + panic("mly_ccb_map: already mapped"); + mc->mc_flags |= MLY_CCB_MAPPED; + + /* Does the command have a data buffer? */ + if (mc->mc_data == NULL) + panic("mly_ccb_map: no data buffer"); +#endif + + rv = bus_dmamap_load(mly->mly_dmat, mc->mc_datamap, mc->mc_data, + mc->mc_length, NULL, BUS_DMA_NOWAIT | BUS_DMA_STREAMING | + ((mc->mc_flags & MLY_CCB_DATAIN) != 0 ? + BUS_DMA_READ : BUS_DMA_WRITE)); + if (rv != 0) + return (rv); + + gen = &mc->mc_packet->generic; + + /* + * Can we use the transfer structure directly? + */ + if ((nseg = mc->mc_datamap->dm_nsegs) <= 2) { + mc->mc_sgoff = -1; + sg = &gen->transfer.direct.sg[0]; + } else { + mc->mc_sgoff = (mc->mc_slot - MLY_SLOT_START) * + MLY_MAX_SEGS; + sg = mly->mly_sg + mc->mc_sgoff; + gen->command_control |= MLY_CMDCTL_EXTENDED_SG_TABLE; + gen->transfer.indirect.entries[0] = htole16(nseg); + gen->transfer.indirect.table_physaddr[0] = + htole64(mly->mly_sg_busaddr + + (mc->mc_sgoff * sizeof(struct mly_sg_entry))); + } + + /* + * Fill the S/G table. + */ + for (ds = mc->mc_datamap->dm_segs; nseg != 0; nseg--, sg++, ds++) { + sg->physaddr = htole64(ds->ds_addr); + sg->length = htole64(ds->ds_len); + } + + /* + * Sync up the data map. + */ + if ((mc->mc_flags & MLY_CCB_DATAIN) != 0) + flg = BUS_DMASYNC_PREREAD; + else /* if ((mc->mc_flags & MLY_CCB_DATAOUT) != 0) */ { + gen->command_control |= MLY_CMDCTL_DATA_DIRECTION; + flg = BUS_DMASYNC_PREWRITE; + } + + bus_dmamap_sync(mly->mly_dmat, mc->mc_datamap, 0, mc->mc_length, flg); + + /* + * Sync up the chained S/G table, if we're using one. + */ + if (mc->mc_sgoff == -1) + return (0); + + bus_dmamap_sync(mly->mly_dmat, mly->mly_sg_dmamap, mc->mc_sgoff, + MLY_SGL_SIZE, BUS_DMASYNC_PREWRITE); + + return (0); +} + +/* + * Unmap a command from controller-visible space. + */ +static void +mly_ccb_unmap(struct mly_softc *mly, struct mly_ccb *mc) +{ + int flg; + +#ifdef DIAGNOSTIC + if ((mc->mc_flags & MLY_CCB_MAPPED) == 0) + panic("mly_ccb_unmap: not mapped"); + mc->mc_flags &= ~MLY_CCB_MAPPED; +#endif + + if ((mc->mc_flags & MLY_CCB_DATAIN) != 0) + flg = BUS_DMASYNC_POSTREAD; + else /* if ((mc->mc_flags & MLY_CCB_DATAOUT) != 0) */ + flg = BUS_DMASYNC_POSTWRITE; + + bus_dmamap_sync(mly->mly_dmat, mc->mc_datamap, 0, mc->mc_length, flg); + bus_dmamap_unload(mly->mly_dmat, mc->mc_datamap); + + if (mc->mc_sgoff == -1) + return; + + bus_dmamap_sync(mly->mly_dmat, mly->mly_sg_dmamap, mc->mc_sgoff, + MLY_SGL_SIZE, BUS_DMASYNC_POSTWRITE); +} + +/* + * Adjust the size of each I/O before it passes to the SCSI layer. + */ +static void +mly_scsipi_minphys(struct buf *bp) +{ + + if (bp->b_bcount > MLY_MAX_XFER) + bp->b_bcount = MLY_MAX_XFER; + minphys(bp); +} + +/* + * Start a SCSI command. + */ +static void +mly_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req, + void *arg) +{ + struct mly_ccb *mc; + struct mly_cmd_scsi_small *ss; + struct scsipi_xfer *xs; + struct scsipi_periph *periph; + struct mly_softc *mly; + struct mly_btl *btl; + int s, tmp; + + mly = (void *)chan->chan_adapter->adapt_dev; + + switch (req) { + case ADAPTER_REQ_RUN_XFER: + xs = arg; + periph = xs->xs_periph; + btl = &mly->mly_btl[chan->chan_channel][periph->periph_target]; + s = splbio(); + tmp = btl->mb_flags; + splx(s); + + /* + * Check for I/O attempt to a protected or non-existant + * device. + */ + if ((tmp & MLY_BTL_PROTECTED) != 0) { + xs->error = XS_SELTIMEOUT; + scsipi_done(xs); + break; + } + +#ifdef DIAGNOSTIC + /* XXX Increase if/when we support large SCSI commands. */ + if (xs->cmdlen > MLY_CMD_SCSI_SMALL_CDB) { + printf("%s: cmd too large\n", mly->mly_dv.dv_xname); + xs->error = XS_DRIVER_STUFFUP; + scsipi_done(xs); + break; + } +#endif + + if (mly_ccb_alloc(mly, &mc)) { + xs->error = XS_RESOURCE_SHORTAGE; + scsipi_done(xs); + break; + } + + /* Build the command. */ + mc->mc_data = xs->data; + mc->mc_length = xs->datalen; + mc->mc_complete = mly_scsipi_complete; + mc->mc_private = xs; + + /* Build the packet for the controller. */ + ss = &mc->mc_packet->scsi_small; + ss->opcode = MDACMD_SCSI; +#ifdef notdef + /* + * XXX FreeBSD does this, but it doesn't fix anything, + * XXX and appears potentially harmful. + */ + ss->command_control |= MLY_CMDCTL_DISABLE_DISCONNECT; +#endif + if ((xs->xs_control & XS_CTL_DATA_OUT) != 0) + mc->mc_flags |= MLY_CCB_DATAOUT; + else if ((xs->xs_control & XS_CTL_DATA_IN) != 0) + mc->mc_flags |= MLY_CCB_DATAIN; + + ss->data_size = htole32(xs->datalen); + _lto3l(MLY_PHYADDR(0, chan->chan_channel, + periph->periph_target, periph->periph_lun), ss->addr); + + if (xs->timeout < 60 * 1000) + ss->timeout = xs->timeout / 1000 | + MLY_TIMEOUT_SECONDS; + else if (xs->timeout < 60 * 60 * 1000) + ss->timeout = xs->timeout / (60 * 1000) | + MLY_TIMEOUT_MINUTES; + else + ss->timeout = xs->timeout / (60 * 60 * 1000) | + MLY_TIMEOUT_HOURS; + + ss->maximum_sense_size = sizeof(xs->sense); + ss->cdb_length = xs->cmdlen; + memcpy(ss->cdb, xs->cmd, xs->cmdlen); + + if (mc->mc_length != 0) + if (mly_ccb_map(mly, mc) != 0) { + xs->error = XS_DRIVER_STUFFUP; + mly_ccb_free(mly, mc); + scsipi_done(xs); + break; + } + + /* + * Give the command to the controller. + */ + if ((xs->xs_control & XS_CTL_POLL) != 0) { + if (mly_ccb_poll(mly, mc, xs->timeout + 5000)) { + xs->error = XS_REQUEUE; + if (mc->mc_length != 0) + mly_ccb_unmap(mly, mc); + mly_ccb_free(mly, mc); + scsipi_done(xs); + } + } else + mly_ccb_enqueue(mly, mc); + + break; + + case ADAPTER_REQ_GROW_RESOURCES: + /* + * Not supported. + */ + break; + + case ADAPTER_REQ_SET_XFER_MODE: + /* + * We can't change the transfer mode, but at least let + * scsipi know what the adapter has negotiated. + */ + mly_get_xfer_mode(mly, chan->chan_channel, arg); + break; + } +} + +/* + * Handle completion of a SCSI command. + */ +static void +mly_scsipi_complete(struct mly_softc *mly, struct mly_ccb *mc) +{ + struct scsipi_xfer *xs; + struct scsipi_channel *chan; + struct scsipi_inquiry_data *inq; + struct mly_btl *btl; + int target, sl, s; + const char *p; + + xs = mc->mc_private; + xs->status = mc->mc_status; + + /* + * XXX The `resid' value as returned by the controller appears to be + * bogus, so we always set it to zero. Is it perhaps the transfer + * count? + */ + xs->resid = 0; /* mc->mc_resid; */ + + if (mc->mc_length != 0) + mly_ccb_unmap(mly, mc); + + switch (mc->mc_status) { + case SCSI_OK: + /* + * In order to report logical device type and status, we + * overwrite the result of the INQUIRY command to logical + * devices. + */ + if (xs->cmd->opcode == INQUIRY) { + chan = xs->xs_periph->periph_channel; + target = xs->xs_periph->periph_target; + btl = &mly->mly_btl[chan->chan_channel][target]; + + s = splbio(); + if ((btl->mb_flags & MLY_BTL_LOGICAL) != 0) { + inq = (struct scsipi_inquiry_data *)xs->data; + mly_padstr(inq->vendor, "MYLEX", 8); + p = mly_describe_code(mly_table_device_type, + btl->mb_type); + mly_padstr(inq->product, p, 16); + p = mly_describe_code(mly_table_device_state, + btl->mb_state); + mly_padstr(inq->revision, p, 4); + } + splx(s); + } + + xs->error = XS_NOERROR; + break; + + case SCSI_CHECK: + sl = mc->mc_sense; + if (sl > sizeof(xs->sense.scsi_sense)) + sl = sizeof(xs->sense.scsi_sense); + memcpy(&xs->sense.scsi_sense, mc->mc_packet, sl); + xs->error = XS_SENSE; + break; + + case SCSI_BUSY: + case SCSI_QUEUE_FULL: + xs->error = XS_BUSY; + break; + + default: + printf("%s: unknown SCSI status 0x%x\n", + mly->mly_dv.dv_xname, xs->status); + xs->error = XS_DRIVER_STUFFUP; + break; + } + + mly_ccb_free(mly, mc); + scsipi_done(xs); +} + +/* + * Notify scsipi about a target's transfer mode. + */ +static void +mly_get_xfer_mode(struct mly_softc *mly, int bus, struct scsipi_xfer_mode *xm) +{ + struct mly_btl *btl; + int s; + + btl = &mly->mly_btl[bus][xm->xm_target]; + xm->xm_mode = 0; + + s = splbio(); + + if ((btl->mb_flags & MLY_BTL_PHYSICAL) != 0) { + if (btl->mb_speed == 0) { + xm->xm_period = 0; + xm->xm_offset = 0; + } else { + xm->xm_period = 12; /* XXX */ + xm->xm_offset = 8; /* XXX */ + xm->xm_mode |= PERIPH_CAP_SYNC; /* XXX */ + } + + switch (btl->mb_width) { + case 32: + xm->xm_mode = PERIPH_CAP_WIDE32; + break; + case 16: + xm->xm_mode = PERIPH_CAP_WIDE16; + break; + default: + xm->xm_mode = 0; + break; + } + } else /* ((btl->mb_flags & MLY_BTL_LOGICAL) != 0) */ { + xm->xm_mode = PERIPH_CAP_WIDE16 | PERIPH_CAP_SYNC; + xm->xm_period = 12; + xm->xm_offset = 8; + } + + if ((btl->mb_flags & MLY_BTL_TQING) != 0) + xm->xm_mode |= PERIPH_CAP_TQING; + + splx(s); + + scsipi_async_event(&mly->mly_chans[bus], ASYNC_EVENT_XFER_MODE, xm); +} + +/* + * ioctl hook; used here only to initiate low-level rescans. + */ +static int +mly_scsipi_ioctl(struct scsipi_channel *chan, u_long cmd, caddr_t data, + int flag, struct proc *p) +{ + struct mly_softc *mly; + int rv; + + mly = (struct mly_softc *)chan->chan_adapter->adapt_dev; + + switch (cmd) { + case SCBUSIOLLSCAN: + mly_scan_channel(mly, chan->chan_channel); + rv = 0; + break; + default: + rv = ENOTTY; + break; + } + + return (rv); +} + +/* + * Handshake with the firmware while the card is being initialised. + */ +static int +mly_fwhandshake(struct mly_softc *mly) +{ + u_int8_t error, param0, param1; + int spinup; + + spinup = 0; + + /* Set HM_STSACK and let the firmware initialise. */ + mly_outb(mly, mly->mly_idbr, MLY_HM_STSACK); + DELAY(1000); /* too short? */ + + /* If HM_STSACK is still true, the controller is initialising. */ + if (!mly_idbr_true(mly, MLY_HM_STSACK)) + return (0); + + printf("%s: controller initialisation started\n", + mly->mly_dv.dv_xname); + + /* + * Spin waiting for initialisation to finish, or for a message to be + * delivered. + */ + while (mly_idbr_true(mly, MLY_HM_STSACK)) { + /* Check for a message */ + if (!mly_error_valid(mly)) + continue; + + error = mly_inb(mly, mly->mly_error_status) & ~MLY_MSG_EMPTY; + param0 = mly_inb(mly, mly->mly_cmd_mailbox); + param1 = mly_inb(mly, mly->mly_cmd_mailbox + 1); + + switch (error) { + case MLY_MSG_SPINUP: + if (!spinup) { + printf("%s: drive spinup in progress\n", + mly->mly_dv.dv_xname); + spinup = 1; + } + break; + + case MLY_MSG_RACE_RECOVERY_FAIL: + printf("%s: mirror race recovery failed - \n", + mly->mly_dv.dv_xname); + printf("%s: one or more drives offline\n", + mly->mly_dv.dv_xname); + break; + + case MLY_MSG_RACE_IN_PROGRESS: + printf("%s: mirror race recovery in progress\n", + mly->mly_dv.dv_xname); + break; + + case MLY_MSG_RACE_ON_CRITICAL: + printf("%s: mirror race recovery on critical drive\n", + mly->mly_dv.dv_xname); + break; + + case MLY_MSG_PARITY_ERROR: + printf("%s: FATAL MEMORY PARITY ERROR\n", + mly->mly_dv.dv_xname); + return (ENXIO); + + default: + printf("%s: unknown initialisation code 0x%x\n", + mly->mly_dv.dv_xname, error); + break; + } + } + + return (0); +} + +/* + * Space-fill a character string + */ +static void +mly_padstr(char *dst, const char *src, int len) +{ + + while (len-- > 0) { + if (*src != '\0') + *dst++ = *src++; + else + *dst++ = ' '; + } +} + +/* + * Allocate DMA safe memory. + */ +static int +mly_dmamem_alloc(struct mly_softc *mly, int size, bus_dmamap_t *dmamap, + caddr_t *kva, bus_addr_t *paddr, bus_dma_segment_t *seg) +{ + int rseg, rv, state; + + state = 0; + + if ((rv = bus_dmamem_alloc(mly->mly_dmat, size, NBPG, 0, + seg, 1, &rseg, BUS_DMA_NOWAIT)) != 0) { + printf("%s: dmamem_alloc = %d\n", mly->mly_dv.dv_xname, rv); + goto bad; + } + + state++; + + if ((rv = bus_dmamem_map(mly->mly_dmat, seg, 1, size, kva, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) { + printf("%s: dmamem_map = %d\n", mly->mly_dv.dv_xname, rv); + goto bad; + } + + state++; + + if ((rv = bus_dmamap_create(mly->mly_dmat, size, size, 1, 0, + BUS_DMA_NOWAIT, dmamap)) != 0) { + printf("%s: dmamap_create = %d\n", mly->mly_dv.dv_xname, rv); + goto bad; + } + + state++; + + if ((rv = bus_dmamap_load(mly->mly_dmat, *dmamap, *kva, size, + NULL, BUS_DMA_NOWAIT)) != 0) { + printf("%s: dmamap_load = %d\n", mly->mly_dv.dv_xname, rv); + goto bad; + } + + *paddr = (*dmamap)->dm_segs[0].ds_addr; + memset(*kva, 0, size); + return (0); + + bad: + if (state > 2) + bus_dmamap_destroy(mly->mly_dmat, *dmamap); + if (state > 1) + bus_dmamem_unmap(mly->mly_dmat, *kva, size); + if (state > 0) + bus_dmamem_free(mly->mly_dmat, seg, 1); + + return (rv); +} + +/* + * Free DMA safe memory. + */ +static void +mly_dmamem_free(struct mly_softc *mly, int size, bus_dmamap_t dmamap, + caddr_t kva, bus_dma_segment_t *seg) +{ + + bus_dmamap_unload(mly->mly_dmat, dmamap); + bus_dmamap_destroy(mly->mly_dmat, dmamap); + bus_dmamem_unmap(mly->mly_dmat, kva, size); + bus_dmamem_free(mly->mly_dmat, seg, 1); +} + + +/* + * Accept an open operation on the control device. + */ +int +mlyopen(dev_t dev, int flag, int mode, struct proc *p) +{ + struct mly_softc *mly; + + if ((mly = device_lookup(&mly_cd, minor(dev))) == NULL) + return (ENXIO); + if ((mly->mly_state & MLY_STATE_INITOK) == 0) + return (ENXIO); + if ((mly->mly_state & MLY_STATE_OPEN) != 0) + return (EBUSY); + + mly->mly_state |= MLY_STATE_OPEN; + return (0); +} + +/* + * Accept the last close on the control device. + */ +int +mlyclose(dev_t dev, int flag, int mode, struct proc *p) +{ + struct mly_softc *mly; + + mly = device_lookup(&mly_cd, minor(dev)); + mly->mly_state &= ~MLY_STATE_OPEN; + return (0); +} + +/* + * Handle control operations. + */ +int +mlyioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct mly_softc *mly; + int rv; + + if (securelevel >= 2) + return (EPERM); + + mly = device_lookup(&mly_cd, minor(dev)); + + switch (cmd) { + case MLYIO_COMMAND: + rv = mly_user_command(mly, (void *)data); + break; + case MLYIO_HEALTH: + rv = mly_user_health(mly, (void *)data); + break; + default: + rv = ENOTTY; + break; + } + + return (rv); +} + +/* + * Execute a command passed in from userspace. + * + * The control structure contains the actual command for the controller, as + * well as the user-space data pointer and data size, and an optional sense + * buffer size/pointer. On completion, the data size is adjusted to the + * command residual, and the sense buffer size to the size of the returned + * sense data. + */ +static int +mly_user_command(struct mly_softc *mly, struct mly_user_command *uc) +{ + struct mly_ccb *mc; + int rv, mapped; + + if ((rv = mly_ccb_alloc(mly, &mc)) != 0) + return (rv); + + mapped = 0; + mc->mc_data = NULL; + + /* + * Handle data size/direction. + */ + if ((mc->mc_length = abs(uc->DataTransferLength)) != 0) { + mc->mc_data = malloc(mc->mc_length, M_DEVBUF, M_WAITOK); + if (mc->mc_data == NULL) { + rv = ENOMEM; + goto out; + } + + if (uc->DataTransferLength > 0) { + mc->mc_flags |= MLY_CCB_DATAIN; + memset(mc->mc_data, 0, mc->mc_length); + } + + if (uc->DataTransferLength < 0) { + mc->mc_flags |= MLY_CCB_DATAOUT; + rv = copyin(uc->DataTransferBuffer, mc->mc_data, + mc->mc_length); + if (rv != 0) + goto out; + } + + if ((rv = mly_ccb_map(mly, mc)) != 0) + goto out; + mapped = 1; + } + + /* Copy in the command and execute it. */ + memcpy(mc->mc_packet, &uc->CommandMailbox, sizeof(uc->CommandMailbox)); + + if ((rv = mly_ccb_wait(mly, mc, 60000)) != 0) + goto out; + + /* Return the data to userspace. */ + if (uc->DataTransferLength > 0) { + rv = copyout(mc->mc_data, uc->DataTransferBuffer, + mc->mc_length); + if (rv != 0) + goto out; + } + + /* Return the sense buffer to userspace. */ + if (uc->RequestSenseLength > 0 && mc->mc_sense > 0) { + rv = copyout(mc->mc_packet, uc->RequestSenseBuffer, + min(uc->RequestSenseLength, mc->mc_sense)); + if (rv != 0) + goto out; + } + + /* Return command results to userspace (caller will copy out). */ + uc->DataTransferLength = mc->mc_resid; + uc->RequestSenseLength = min(uc->RequestSenseLength, mc->mc_sense); + uc->CommandStatus = mc->mc_status; + rv = 0; + + out: + if (mapped) + mly_ccb_unmap(mly, mc); + if (mc->mc_data != NULL) + free(mc->mc_data, M_DEVBUF); + if (mc != NULL) + mly_ccb_free(mly, mc); + + return (rv); +} + +/* + * Return health status to userspace. If the health change index in the + * user structure does not match that currently exported by the controller, + * we return the current status immediately. Otherwise, we block until + * either interrupted or new status is delivered. + */ +static int +mly_user_health(struct mly_softc *mly, struct mly_user_health *uh) +{ + struct mly_health_status mh; + int rv, s; + + /* Fetch the current health status from userspace. */ + rv = copyin(uh->HealthStatusBuffer, &mh, sizeof(mh)); + if (rv != 0) + return (rv); + + /* spin waiting for a status update */ + s = splbio(); + if (mly->mly_event_change == mh.change_counter) + rv = tsleep(&mly->mly_event_change, PRIBIO | PCATCH, + "mlyhealth", 0); + splx(s); + + if (rv == 0) { + /* + * Copy the controller's health status buffer out (there is + * a race here if it changes again). + */ + rv = copyout(&mly->mly_mmbox->mmm_health.status, + uh->HealthStatusBuffer, sizeof(uh->HealthStatusBuffer)); + } + + return (rv); +} diff --git a/sys/dev/pci/mly_tables.h b/sys/dev/pci/mly_tables.h new file mode 100644 index 00000000000..a8520996adf --- /dev/null +++ b/sys/dev/pci/mly_tables.h @@ -0,0 +1,375 @@ +/* $NetBSD: mly_tables.h,v 1.1 2001/07/30 19:59:07 ad Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Andrew Doran, Thor Lancelot Simon, and Eric Haszlakiewicz. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/*- + * Copyright (c) 2000 Michael Smith + * Copyright (c) 2000 BSDi + * 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. + * + * from FreeBSD: mly_tables.h,v 1.2 2001/07/14 00:12:22 msmith Exp + */ + +#ifndef _PCI_MLY_TABLES_H_ +#define _PCI_MLY_TABLES_H_ + +struct mly_code_lookup { + const char *string; + u_int code; +}; + +static const char *mly_describe_code(const struct mly_code_lookup *, + u_int32_t); + +/* + * Look up a text description of a numeric code and return a pointer to same. + */ +static const char * +mly_describe_code(const struct mly_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); +} + +/* + * This table is directly derived from the corresponding table in the Linux + * driver, and uses a derivative encoding for simplicity's sake. + * + * The first character of the string determines the format of the message. + * + * p "physical device <channel>:<target> <text>" (physical device status) + * s "physical device <channel>:<target> <text>" (scsi message or error) + * " sense key <key> asc <asc> ascq <ascq>" + * " info <info> csi <csi>" + * l "logical drive <unit>: <text>" (logical device status) + * m "logical drive <unit>: <text>" (logical device message) + * + * Messages which are typically suppressed have the first character + * capitalised. These messages will only be printed if bootverbose is set. + * + * The second character in the string indicates an action to be taken as a + * result of the event. + * + * r rescan the device for possible state change + * + */ +static const struct mly_code_lookup mly_table_event[] = { + /* + * Physical device events (0x0000 - 0x007f). + */ + { "pr online", 0x0001 }, + { "pr standby", 0x0002 }, + { "p automatic rebuild started", 0x0005 }, + { "p manual rebuild started", 0x0006 }, + { "pr rebuild completed", 0x0007 }, + { "pr rebuild cancelled", 0x0008 }, + { "pr rebuild failed for unknown reasons", 0x0009 }, + { "pr rebuild failed due to new physical device", 0x000a }, + { "pr rebuild failed due to logical drive failure", 0x000b }, + { "sr offline", 0x000c }, + { "pr found", 0x000d }, + { "pr gone", 0x000e }, + { "p unconfigured", 0x000f }, + { "p expand capacity started", 0x0010 }, + { "pr expand capacity completed", 0x0011 }, + { "pr expand capacity failed", 0x0012 }, + { "p parity error", 0x0016 }, + { "p soft error", 0x0017 }, + { "p miscellaneous error", 0x0018 }, + { "p reset", 0x0019 }, + { "p active spare found", 0x001a }, + { "p warm spare found", 0x001b }, + { "s sense data received", 0x001c }, + { "p initialization started", 0x001d }, + { "pr initialization completed", 0x001e }, + { "pr initialization failed", 0x001f }, + { "pr initialization cancelled", 0x0020 }, + { "P write recovery failed", 0x0021 }, + { "p scsi bus reset failed", 0x0022 }, + { "p double check condition", 0x0023 }, + { "p device cannot be accessed", 0x0024 }, + { "p gross error on scsi processor", 0x0025 }, + { "p bad tag from device", 0x0026 }, + { "p command timeout", 0x0027 }, + { "pr system reset", 0x0028 }, + { "p busy status or parity error", 0x0029 }, + { "pr host set device to failed state", 0x002a }, + { "pr selection timeout", 0x002b }, + { "p scsi bus phase error", 0x002c }, + { "pr device returned unknown status", 0x002d }, + { "pr device not ready", 0x002e }, + { "p device not found at startup", 0x002f }, + { "p COD write operation failed", 0x0030 }, + { "p BDT write operation failed", 0x0031 }, + { "p missing at startup", 0x0039 }, + { "p start rebuild failed (physical drive too small)", 0x003a }, + + /* + * Logical device events (0x0080 - 0x00ff). + */ + { "m consistency check started", 0x0080 }, + { "mr consistency check completed", 0x0081 }, + { "mr consistency check cancelled", 0x0082 }, + { "mr consistency check completed with errors", 0x0083 }, + { "mr consistency check failed (logical drive failure)",0x0084 }, + { "mr consistency check failed (physical device failure)",0x0085 }, + { "lr offline", 0x0086 }, + { "lr critical", 0x0087 }, + { "lr online", 0x0088 }, + { "m automatic rebuild started", 0x0089 }, + { "m manual rebuild started", 0x008a }, + { "mr rebuild completed", 0x008b }, + { "mr rebuild cancelled", 0x008c }, + { "mr rebuild failed for unknown reasons", 0x008d }, + { "mr rebuild failed due to new physical device", 0x008e }, + { "mr rebuild failed due to logical drive failure", 0x008f }, + { "l initialization started", 0x0090 }, + { "lr initialization completed", 0x0091 }, + { "lr initialization cancelled", 0x0092 }, + { "lr initialization failed", 0x0093 }, + { "lr found", 0x0094 }, + { "lr gone", 0x0095 }, + { "l expand capacity started", 0x0096 }, + { "lr expand capacity completed", 0x0097 }, + { "lr expand capacity failed", 0x0098 }, + { "l bad block found", 0x0099 }, + { "lr size changed", 0x009a }, + { "lr type changed", 0x009b }, + { "l bad data block found", 0x009c }, + { "l read of data block in bdt", 0x009e }, + { "l write back data for disk block lost", 0x009f }, + + /* + * Enclosure management events (0x0100 - 0x017f). + */ + { "e enclosure %d fan %d failed", 0x0140 }, + { "e enclosure %d fan %d ok", 0x0141 }, + { "e enclosure %d fan %d not present", 0x0142 }, + { "e enclosure %d power supply %d failed", 0x0143 }, + { "e enclosure %d power supply %d ok", 0x0144 }, + { "e enclosure %d power supply %d not present", 0x0145 }, + { "e enclosure %d temperature sensor %d failed", 0x0146 }, + { "e enclosure %d temperature sensor %d critical", 0x0147 }, + { "e enclosure %d temperature sensor %d ok", 0x0148 }, + { "e enclosure %d temperature sensor %d not present", 0x0149 }, + { "e enclosure %d unit %d access critical", 0x014a }, + { "e enclosure %d unit %d access ok", 0x014b }, + { "e enclosure %d unit %d access offline", 0x014c }, + + /* + * Controller events (0x0180 - 0x01ff). + */ + { "c cache write back error", 0x0181 }, + { "c battery backup unit found", 0x0188 }, + { "c battery backup unit charge level low", 0x0189 }, + { "c battery backup unit charge level ok", 0x018a }, + { "c installation aborted", 0x0193 }, + { "c mirror race recovery in progress", 0x0195 }, + { "c mirror race on critical drive", 0x0196 }, + { "c memory soft ecc error", 0x019e }, + { "c memory hard ecc error", 0x019f }, + { "c battery backup unit failed", 0x01a2 }, + + { NULL, 0 }, + { "? unknown event code", 0 } +}; + +/* + * Values here must be 16 characters or less, as they are packed into + * the 'product' field in the SCSI inquiry data. + */ +static const struct mly_code_lookup mly_table_device_state[] = { + { "OFLN", MLY_DEVICE_STATE_OFFLINE }, + { "UNCF", MLY_DEVICE_STATE_UNCONFIGURED }, + { "ONLN", MLY_DEVICE_STATE_ONLINE }, + { "CRIT", MLY_DEVICE_STATE_CRITICAL }, + { "NORD", MLY_DEVICE_STATE_WRITEONLY }, + { "STBY", MLY_DEVICE_STATE_STANDBY }, + { "MISS", MLY_DEVICE_STATE_MISSING }, + { NULL, 0 }, + { "????", 0 } +}; + +/* + * Values here must be 8 characters or less, as they are packed into the + * 'vendor' field in the SCSI inquiry data. + */ +static const struct mly_code_lookup mly_table_device_type[] = { + { "RAID 0", MLY_DEVICE_TYPE_RAID0 }, + { "RAID 1", MLY_DEVICE_TYPE_RAID1 }, + { "RAID 3", MLY_DEVICE_TYPE_RAID3 }, + { "RAID 5", MLY_DEVICE_TYPE_RAID5 }, + { "RAID 6", MLY_DEVICE_TYPE_RAID6 }, + { "RAID 7", MLY_DEVICE_TYPE_RAID7 }, + { "SPAN", MLY_DEVICE_TYPE_NEWSPAN }, + { "RAID 3", MLY_DEVICE_TYPE_RAID3F }, + { "RAID 3", MLY_DEVICE_TYPE_RAID3L }, + { "SPAN", MLY_DEVICE_TYPE_SPAN }, + { "RAID 5", MLY_DEVICE_TYPE_RAID5L }, + { "RAID E", MLY_DEVICE_TYPE_RAIDE }, + { "PHYSICAL", MLY_DEVICE_TYPE_PHYSICAL }, + { NULL, 0 }, + { "UNKNOWN", 0 } +}; + +#ifdef notused +static struct mly_code_lookup mly_table_bustype[] = { + { "SCSI", 0x00 }, + { "FC-AL", 0x01 }, + { "PCI", 0x03 }, + { NULL, 0 }, + { "unknown bus", 0} +}; + +static const struct mly_code_lookup mly_table_controllertype[] = { +#if 0 /* not supported by this driver */ + { "DAC960E", 0x01 }, /* EISA */ + { "DAC960M", 0x08 }, /* MCA */ + { "DAC960PD", 0x10 }, /* PCI Dual */ + { "DAC960PL", 0x11 }, /* PCU low-cost */ + { "DAC960PDU", 0x12 }, /* PD Ultra */ + { "DAC960PE", 0x13 }, /* Peregrine low-cost */ + { "DAC960PG", 0x14 }, /* Peregrine high-performance */ + { "DAC960PJ", 0x15 }, /* Road Runner */ + { "DAC960PTL0", 0x16 }, /* Jaguar */ + { "DAC960PR", 0x17 }, /* Road Runner (again?) */ + { "DAC960PRL", 0x18 }, /* Tomcat */ + { "DAC960PT", 0x19 }, /* Road Runner (yet again?) */ + { "DAC1164P", 0x1a }, /* Little Apple */ + { "DAC960PTL1", 0x1b }, /* Jaguar+ */ +#endif + { "EXR2000P", 0x1c }, /* Big Apple */ + { "EXR3000P", 0x1d }, /* Fibre Apple */ + { "AcceleRAID 352", 0x1e }, /* Leopard */ + { "AcceleRAID 170", 0x1f }, /* Lynx */ + { "AcceleRAID 160", 0x20 }, /* Bobcat */ + { NULL, 0 }, + { "unknown adapter", 0 } +}; + +static const struct mly_code_lookup mly_table_oemname[] = { + { "Mylex", MLY_OEM_MYLEX }, + { "IBM", MLY_OEM_IBM }, + { "Hewlett-Packard", MLY_OEM_HP }, + { "DEC/Compaq", MLY_OEM_DEC }, + { "Siemens", MLY_OEM_SIEMENS }, + { "Intel", MLY_OEM_INTEL }, + { NULL, 0 }, + { "unknown OEM", 0 } +}; + +static const struct mly_code_lookup mly_table_memorytype[] = { + { "DRAM", 0x01 }, + { "EDRAM", 0x02 }, + { "EDO RAM", 0x03 }, + { "SDRAM", 0x04 }, + { NULL, 0 }, + { "unknown memory", 0 } +}; + +static const struct mly_code_lookup mly_table_cputype[] = { + { "i960CA", 0x01 }, + { "i960RD", 0x02 }, + { "i960RN", 0x03 }, + { "i960RP", 0x04 }, + { "NorthBay(?)", 0x05 }, + { "StrongArm", 0x06 }, + { "i960RM", 0x07 }, + { NULL, 0 }, + { "unknown CPU", 0 } +}; + +static const struct mly_code_lookup mly_table_stripe_size[] = { + { "NONE", MLY_STRIPE_ZERO }, + { "512B", MLY_STRIPE_512b }, + { "1k", MLY_STRIPE_1k }, + { "2k", MLY_STRIPE_2k }, + { "4k", MLY_STRIPE_4k }, + { "8k", MLY_STRIPE_8k }, + { "16k", MLY_STRIPE_16k }, + { "32k", MLY_STRIPE_32k }, + { "64k", MLY_STRIPE_64k }, + { "128k", MLY_STRIPE_128k }, + { "256k", MLY_STRIPE_256k }, + { "512k", MLY_STRIPE_512k }, + { "1M", MLY_STRIPE_1m }, + { NULL, 0 }, + { "unknown", 0 } +}; + +static const struct mly_code_lookup mly_table_cacheline_size[] = { + { "NONE", MLY_CACHELINE_ZERO }, + { "512B", MLY_CACHELINE_512b }, + { "1k", MLY_CACHELINE_1k }, + { "2k", MLY_CACHELINE_2k }, + { "4k", MLY_CACHELINE_4k }, + { "8k", MLY_CACHELINE_8k }, + { "16k", MLY_CACHELINE_16k }, + { "32k", MLY_CACHELINE_32k }, + { "64k", MLY_CACHELINE_64k }, + { NULL, 0 }, + { "unknown", 0 } +}; +#endif /* notused */ + +#endif /* !defined _PCI_MLY_TABLES_H_ */ diff --git a/sys/dev/pci/mlyio.h b/sys/dev/pci/mlyio.h new file mode 100644 index 00000000000..b1ba5826b9c --- /dev/null +++ b/sys/dev/pci/mlyio.h @@ -0,0 +1,111 @@ +/* $NetBSD: mlyio.h,v 1.1 2001/07/30 19:59:06 ad Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Andrew Doran, Thor Lancelot Simon, and Eric Haszlakiewicz. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/*- + * Copyright (c) 2001 Michael Smith + * 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. + * + * from FreeBSD: mlyio.h,v 1.2 2001/07/14 00:12:22 msmith Exp + */ + +/* + * Control structures exchanged through the GAM interface with userland + * management tools. + * + * The member naming here is consistent with the Linux driver, with which this + * interface is basically compatible. + */ + +#ifndef _PCI_MLYIO_H_ +#define _PCI_MLYIO_H_ + +struct mly_user_command { + unsigned char ControllerNumber; + union mly_cmd_packet CommandMailbox; + int DataTransferLength; + int RequestSenseLength; + void *DataTransferBuffer; + void *RequestSenseBuffer; + int CommandStatus; /* not in the Linux structure */ +}; + +#define MLYIO_COMMAND _IOWR('M', 200, struct mly_user_command) + +struct mly_user_health { + unsigned char ControllerNumber; + void *HealthStatusBuffer; +}; + +#define MLYIO_HEALTH _IOW('M', 201, struct mly_user_health) + +/* + * Command queue statistics + */ + +#define MLYQ_FREE 0 +#define MLYQ_BUSY 1 +#define MLYQ_COMPLETE 2 +#define MLYQ_COUNT 3 + +struct mly_qstat { + u_int32_t q_length; + u_int32_t q_max; +}; + +#endif /* !defined _PCI_MLYIO_ */ diff --git a/sys/dev/pci/mlyreg.h b/sys/dev/pci/mlyreg.h new file mode 100644 index 00000000000..57036ba65b2 --- /dev/null +++ b/sys/dev/pci/mlyreg.h @@ -0,0 +1,1313 @@ +/* $NetBSD: mlyreg.h,v 1.1 2001/07/30 19:59:07 ad Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Andrew Doran, Thor Lancelot Simon, and Eric Haszlakiewicz. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/*- + * Copyright (c) 2000 Michael Smith + * Copyright (c) 2000 BSDi + * 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. + * + * from FreeBSD: mlyreg.h,v 1.1 2000/08/23 03:22:39 msmith Exp + */ + +/* + * Section numbers in this document refer to the Mylex "Firmware Software Interface" + * document ('FSI'), revision 0.11 04/11/00 unless otherwise qualified. + * + * Reference is made to the Mylex "Programming Guide for 6.x Controllers" document + * ('PG6'), document #771242 revision 0.02, 04/11/00 + * + * Note that fields marked N/A are not supported by the PCI controllers, but are + * defined here to hold place in datastructures that are shared with the SCSI + * controllers. Items not relevant to PCI controllers are not described here. + * + * Ordering of items in this file is a little odd due to the constraints of + * nested declarations. + */ + +#ifndef _PCI_MLYREG_H_ +#define _PCI_MLYREG_H_ + +/* + * 2.1 (Scatter Gather List Format) + */ +struct mly_sg_entry { + u_int64_t physaddr; + u_int64_t length; +} __attribute__ ((__packed__)); + +/* + * 5.2 System Device Access + * + * This is corroborated by the layout of the MDACIOCTL_GETCONTROLLERINFO data + * in 21.8 + */ +#define MLY_MAX_CHANNELS 6 +#define MLY_MAX_TARGETS 16 +#define MLY_MAX_LUNS 1 + +/* + * 8.1 Different Device States + */ +#define MLY_DEVICE_STATE_OFFLINE 0x08 /* DEAD/OFFLINE */ +#define MLY_DEVICE_STATE_UNCONFIGURED 0x00 +#define MLY_DEVICE_STATE_ONLINE 0x01 +#define MLY_DEVICE_STATE_CRITICAL 0x09 +#define MLY_DEVICE_STATE_WRITEONLY 0x03 +#define MLY_DEVICE_STATE_STANDBY 0x21 +#define MLY_DEVICE_STATE_MISSING 0x04 /* or-ed with (ONLINE or WRITEONLY or STANDBY) */ + +/* + * 8.2 Device Type Field definitions + */ +#define MLY_DEVICE_TYPE_RAID0 0x0 /* RAID 0 */ +#define MLY_DEVICE_TYPE_RAID1 0x1 /* RAID 1 */ +#define MLY_DEVICE_TYPE_RAID3 0x3 /* RAID 3 right asymmetric parity */ +#define MLY_DEVICE_TYPE_RAID5 0x5 /* RAID 5 right asymmetric parity */ +#define MLY_DEVICE_TYPE_RAID6 0x6 /* RAID 6 (Mylex RAID 6) */ +#define MLY_DEVICE_TYPE_RAID7 0x7 /* RAID 7 (JBOD) */ +#define MLY_DEVICE_TYPE_NEWSPAN 0x8 /* New Mylex SPAN */ +#define MLY_DEVICE_TYPE_RAID3F 0x9 /* RAID 3 fixed parity */ +#define MLY_DEVICE_TYPE_RAID3L 0xb /* RAID 3 left symmetric parity */ +#define MLY_DEVICE_TYPE_SPAN 0xc /* current spanning implementation */ +#define MLY_DEVICE_TYPE_RAID5L 0xd /* RAID 5 left symmetric parity */ +#define MLY_DEVICE_TYPE_RAIDE 0xe /* RAID E (concatenation) */ +#define MLY_DEVICE_TYPE_PHYSICAL 0xf /* physical device */ + +/* + * 8.3 Stripe Size + */ +#define MLY_STRIPE_ZERO 0x0 /* no stripe (RAID 1, RAID 7, etc) */ +#define MLY_STRIPE_512b 0x1 +#define MLY_STRIPE_1k 0x2 +#define MLY_STRIPE_2k 0x3 +#define MLY_STRIPE_4k 0x4 +#define MLY_STRIPE_8k 0x5 +#define MLY_STRIPE_16k 0x6 +#define MLY_STRIPE_32k 0x7 +#define MLY_STRIPE_64k 0x8 +#define MLY_STRIPE_128k 0x9 +#define MLY_STRIPE_256k 0xa +#define MLY_STRIPE_512k 0xb +#define MLY_STRIPE_1m 0xc + +/* + * 8.4 Cacheline Size + */ +#define MLY_CACHELINE_ZERO 0x0 /* caching cannot be enabled */ +#define MLY_CACHELINE_512b 0x1 +#define MLY_CACHELINE_1k 0x2 +#define MLY_CACHELINE_2k 0x3 +#define MLY_CACHELINE_4k 0x4 +#define MLY_CACHELINE_8k 0x5 +#define MLY_CACHELINE_16k 0x6 +#define MLY_CACHELINE_32k 0x7 +#define MLY_CACHELINE_64k 0x8 + +/* + * 8.5 Read/Write control + */ +#define MLY_RWCtl_INITTED (1<<7) /* if set, the logical device is initialised */ + /* write control */ +#define MLY_RWCtl_WCD (0) /* write cache disabled */ +#define MLY_RWCtl_WDISABLE (1<<3) /* writing disabled */ +#define MLY_RWCtl_WCE (2<<3) /* write cache enabled */ +#define MLY_RWCtl_IWCE (3<<3) /* intelligent write cache enabled */ + /* read control */ +#define MLY_RWCtl_RCD (0) /* read cache is disabled */ +#define MLY_RWCtl_RCE (1) /* read cache enabled */ +#define MLY_RWCtl_RAHEAD (2) /* readahead enabled */ +#define MLY_RWCtl_IRAHEAD (3) /* intelligent readahead enabled */ + +/* + * 9.0 LUN Map Format + */ +struct mly_lun_map { + u_int8_t res1:4; + u_int8_t host_port_mapped:1; /* this system drive visibile to host on this controller/port combination */ + u_int8_t tid_valid:1; /* target ID valid */ + u_int8_t hid_valid:1; /* host ID valid */ + u_int8_t lun_valid:1; /* LUN valid */ + u_int8_t res2; + u_int8_t lun; /* LUN */ + u_int8_t tid; /* TID */ + u_int8_t hid[32]; /* HID (one bit for each host) */ +} __attribute__ ((__packed__)); + +/* + * 10.1 Controller Parameters + */ +struct mly_param_controller { + u_int8_t rdahen:1; /* N/A */ + u_int8_t bilodly:1; /* N/A */ + u_int8_t fua_disable:1; + u_int8_t reass1s:1; /* N/A */ + u_int8_t truvrfy:1; /* N/A */ + u_int8_t dwtvrfy:1; /* N/A */ + u_int8_t background_initialisation:1; + u_int8_t clustering:1; /* N/A */ + + u_int8_t bios_disable:1; + u_int8_t boot_from_cdrom:1; + u_int8_t drive_coercion:1; + u_int8_t write_same_disable:1; + u_int8_t hba_mode:1; /* N/A */ + u_int8_t bios_geometry:2; +#define MLY_BIOSGEOM_2G 0x0 +#define MLY_BIOSGEOM_8G 0x1 + u_int8_t res1:1; /* N/A */ + + u_int8_t res2[2]; /* N/A */ + + u_int8_t v_dec:1; + u_int8_t safte:1; /* N/A */ + u_int8_t ses:1; /* N/A */ + u_int8_t res3:2; /* N/A */ + u_int8_t v_arm:1; + u_int8_t v_ofm:1; + u_int8_t res4:1; /* N/A */ + + u_int8_t rebuild_check_rate; + u_int8_t cache_line_size; /* see 8.4 */ + u_int8_t oem_code; +#define MLY_OEM_MYLEX 0x00 +#define MLY_OEM_IBM 0x08 +#define MLY_OEM_HP 0x0a +#define MLY_OEM_DEC 0x0c +#define MLY_OEM_SIEMENS 0x10 +#define MLY_OEM_INTEL 0x12 + u_int8_t spinup_mode; +#define MLY_SPIN_AUTO 0 +#define MLY_SPIN_PWRSPIN 1 +#define MLY_SPIN_WSSUSPIN 2 + u_int8_t spinup_devices; + u_int8_t spinup_interval; + u_int8_t spinup_wait_time; + + u_int8_t res5:3; /* N/A */ + u_int8_t vutursns:1; /* N/A */ + u_int8_t dccfil:1; /* N/A */ + u_int8_t nopause:1; /* N/A */ + u_int8_t disqfull:1; /* N/A */ + u_int8_t disbusy:1; /* N/A */ + + u_int8_t res6:2; /* N/A */ + u_int8_t failover_node_name; /* N/A */ + u_int8_t res7:1; /* N/A */ + u_int8_t ftopo:3; /* N/A */ + u_int8_t disable_ups:1; /* N/A */ + + u_int8_t res8:1; /* N/A */ + u_int8_t propagate_reset:1; /* N/A */ + u_int8_t nonstd_mp_reset:1; /* N/A */ + u_int8_t res9:5; /* N/A */ + + u_int8_t res10; /* N/A */ + u_int8_t serial_port_baud_rate; /* N/A */ + u_int8_t serial_port_control; /* N/A */ + u_int8_t change_stripe_ok_developer_flag_only; /* N/A */ + + u_int8_t small_large_host_transfers:2; /* N/A */ + u_int8_t frame_control:2; /* N/A */ + u_int8_t pci_latency_control:2; /* N/A */ + u_int8_t treat_lip_as_reset:1; /* N/A */ + u_int8_t res11:1; /* N/A */ + + u_int8_t ms_autorest:1; /* N/A */ + u_int8_t res12:7; /* N/A */ + + u_int8_t ms_aa_fsim:1; /* N/A */ + u_int8_t ms_aa_ccach:1; /* N/A */ + u_int8_t ms_aa_fault_signals:1; /* N/A */ + u_int8_t ms_aa_c4_faults:1; /* N/A */ + u_int8_t ms_aa_host_reset_delay_mask:4; /* N/A */ + + u_int8_t ms_flg_simplex_no_rstcom:1; /* N/A */ + u_int8_t res13:7; /* N/A */ + + u_int8_t res14; /* N/A */ + u_int8_t hardloopid[2][2]; /* N/A */ + u_int8_t ctrlname[2][16+1]; /* N/A */ + u_int8_t initiator_id; + u_int8_t startup_option; +#define MLY_STARTUP_IF_NO_CHANGE 0x0 +#define MLY_STARTUP_IF_NO_LUN_CHANGE 0x1 +#define MLY_STARTUP_IF_NO_LUN_OFFLINE 0x2 +#define MLY_STARTUP_IF_LUN0_NO_CHANGE 0x3 +#define MLY_STARTUP_IF_LUN0_NOT_OFFLINE 0x4 +#define MLY_STARTUP_ALWAYS 0x5 + + u_int8_t res15[62]; +} __attribute__ ((__packed__)); + +/* + * 10.2 Physical Device Parameters + */ +struct mly_param_physical_device { + u_int16_t tags; + u_int16_t speed; + u_int8_t width; + u_int8_t combing:1; + u_int8_t res1:7; + u_int8_t res2[3]; +} __attribute__ ((__packed__)); + +/* + * 10.3 Logical Device Parameters + */ +struct mly_param_logical_device { + u_int8_t type; /* see 8.2 */ + u_int8_t state; /* see 8.1 */ + u_int16_t raid_device; + u_int8_t res1; + u_int8_t bios_geometry; /* BIOS control word? */ + u_int8_t stripe_size; /* see 8.3 */ + u_int8_t read_write_control; /* see 8.5 */ + u_int8_t res2[8]; +} __attribute__ ((__packed__)); + +/* + * 12.3 Health Status Buffer + * + * Pad to 128 bytes. + */ +struct mly_health_status { + u_int32_t uptime_us; /* N/A */ + u_int32_t uptime_ms; /* N/A */ + u_int32_t realtime; /* N/A */ + u_int32_t res1; /* N/A */ + u_int32_t change_counter; + u_int32_t res2; /* N/A */ + u_int32_t debug_message_index; /* N/A */ + u_int32_t bios_message_index; /* N/A */ + u_int32_t trace_page; /* N/A */ + u_int32_t profiler_page; /* N/A */ + u_int32_t next_event; + u_int8_t res3[4 + 16 + 64]; /* N/A */ +} __attribute__ ((__packed__)); + +/* + * 14.2 Timeout Bit Format + */ +#define MLY_TIMEOUT_SECONDS 0x00 +#define MLY_TIMEOUT_MINUTES 0x40 +#define MLY_TIMEOUT_HOURS 0x80 + +/* + * 14.3 Operation Device + */ +#define MLY_OPDEVICE_PHYSICAL_DEVICE 0x0 +#define MLY_OPDEVICE_RAID_DEVICE 0x1 +#define MLY_OPDEVICE_PHYSICAL_CHANNEL 0x2 +#define MLY_OPDEVICE_RAID_CHANNEL 0x3 +#define MLY_OPDEVICE_PHYSICAL_CONTROLLER 0x4 +#define MLY_OPDEVICE_RAID_CONTROLLER 0x5 +#define MLY_OPDEVICE_CONFIGURATION_GROUP 0x10 + +/* + * 14.4 Status Bit Format + * + * AKA Status Mailbox Format + * + * XXX format conflict between FSI and PG6 over the ordering of the + * status and sense length fields. + */ +struct mly_status { + u_int16_t command_id; + u_int8_t status; + u_int8_t sense_length; + int32_t residue; +} __attribute__ ((__packed__)); + +/* + * 14.5 Command Control Bit (CCB) format + * + * This byte is unfortunately named. + */ +#define MLY_CMDCTL_FORCE_UNIT_ACCESS 0x01 +#define MLY_CMDCTL_DISABLE_PAGE_OUT 0x02 +#define MLY_CMDCTL_EXTENDED_SG_TABLE 0x08 +#define MLY_CMDCTL_DATA_DIRECTION 0x10 +#define MLY_CMDCTL_NO_AUTO_SENSE 0x40 +#define MLY_CMDCTL_DISABLE_DISCONNECT 0x80 + +/* + * 15.0 Commands + * + * We use the command names as given by Mylex + */ +#define MDACMD_MEMCOPY 0x1 /* memory to memory copy */ +#define MDACMD_SCSIPT 0x2 /* SCSI passthrough (small command) */ +#define MDACMD_SCSILCPT 0x3 /* SCSI passthrough (large command) */ +#define MDACMD_SCSI 0x4 /* SCSI command for logical/phyiscal device (small command) */ +#define MDACMD_SCSILC 0x5 /* SCSI command for logical/phyiscal device (large command) */ +#define MDACMD_IOCTL 0x20 /* Management command */ +#define MDACMD_IOCTLCHECK 0x23 /* Validate management command (not implemented) */ + +/* + * 16.0 IOCTL command + * + * We use the IOCTL names as given by Mylex + * Note that only ioctls supported by the PCI controller family are listed + */ +#define MDACIOCTL_GETCONTROLLERINFO 0x1 +#define MDACIOCTL_GETLOGDEVINFOVALID 0x3 +#define MDACIOCTL_GETPHYSDEVINFOVALID 0x5 +#define MDACIOCTL_GETCONTROLLERSTATISTICS 0xb +#define MDACIOCTL_GETLOGDEVSTATISTICS 0xd +#define MDACIOCTL_GETPHYSDEVSTATISTICS 0xf +#define MDACIOCTL_GETHEALTHSTATUS 0x11 +#define MDACIOCTL_GETEVENT 0x15 +/* flash update */ +#define MDACIOCTL_STOREIMAGE 0x2c +#define MDACIOCTL_READIMAGE 0x2d +#define MDACIOCTL_FLASHIMAGES 0x2e +/* battery backup unit */ +#define MDACIOCTL_GET_SUBSYSTEM_DATA 0x70 +#define MDACIOCTL_SET_SUBSYSTEM_DATA 0x71 +/* non-data commands */ +#define MDACIOCTL_STARTDISOCVERY 0x81 +#define MDACIOCTL_SETRAIDDEVSTATE 0x82 +#define MDACIOCTL_INITPHYSDEVSTART 0x84 +#define MDACIOCTL_INITPHYSDEVSTOP 0x85 +#define MDACIOCTL_INITRAIDDEVSTART 0x86 +#define MDACIOCTL_INITRAIDDEVSTOP 0x87 +#define MDACIOCTL_REBUILDRAIDDEVSTART 0x88 +#define MDACIOCTL_REBUILDRAIDDEVSTOP 0x89 +#define MDACIOCTL_MAKECONSISTENTDATASTART 0x8a +#define MDACIOCTL_MAKECONSISTENTDATASTOP 0x8b +#define MDACIOCTL_CONSISTENCYCHECKSTART 0x8c +#define MDACIOCTL_CONSISTENCYCHECKSTOP 0x8d +#define MDACIOCTL_SETMEMORYMAILBOX 0x8e +#define MDACIOCTL_RESETDEVICE 0x90 +#define MDACIOCTL_FLUSHDEVICEDATA 0x91 +#define MDACIOCTL_PAUSEDEVICE 0x92 +#define MDACIOCTL_UNPAUSEDEVICE 0x93 +#define MDACIOCTL_LOCATEDEVICE 0x94 +#define MDACIOCTL_SETMASTERSLAVEMODE 0x95 +#define MDACIOCTL_SETREALTIMECLOCK 0xac +/* RAID configuration */ +#define MDACIOCTL_CREATENEWCONF 0xc0 +#define MDACIOCTL_DELETERAIDDEV 0xc1 +#define MDACIOCTL_REPLACEINTERNALDEV 0xc2 +#define MDACIOCTL_RENAMERAIDDEV 0xc3 +#define MDACIOCTL_ADDNEWCONF 0xc4 +#define MDACIOCTL_XLATEPHYSDEVTORAIDDEV 0xc5 +#define MDACIOCTL_MORE 0xc6 +#define MDACIOCTL_SETPHYSDEVPARAMETER 0xc8 +#define MDACIOCTL_GETPHYSDEVPARAMETER 0xc9 +#define MDACIOCTL_CLEARCONF 0xca +#define MDACIOCTL_GETDEVCONFINFO 0xcb +#define MDACIOCTL_GETGROUPCONFINFO 0xcc +#define MDACIOCTL_GETFREESPACELIST 0xcd +#define MDACIOCTL_GETLOGDEVPARAMETER 0xce +#define MDACIOCTL_SETLOGDEVPARAMETER 0xcf +#define MDACIOCTL_GETCONTROLLERPARAMETER 0xd0 +#define MDACIOCTL_SETCONTRLLERPARAMETER 0xd1 +#define MDACIOCTL_CLEARCONFSUSPMODE 0xd2 +#define MDACIOCTL_GETBDT_FOR_SYSDRIVE 0xe0 + +/* + * 17.1.4 Data Transfer Memory Address Without SG List + */ +struct mly_short_transfer { + struct mly_sg_entry sg[2]; +} __attribute__ ((__packed__)); + +/* + * 17.1.5 Data Transfer Memory Address With SG List + * + * Note that only the first s/g table is currently used. + */ +struct mly_sg_transfer { + u_int16_t entries[3]; + u_int16_t res1; + u_int64_t table_physaddr[3]; +} __attribute__ ((__packed__)); + +/* + * 17.1.3 Data Transfer Memory Address Format + */ +union mly_cmd_transfer { + struct mly_short_transfer direct; + struct mly_sg_transfer indirect; +}; + +/* + * 21.1 MDACIOCTL_SETREALTIMECLOCK + * 21.7 MDACIOCTL_GETHEALTHSTATUS + * 21.8 MDACIOCTL_GETCONTROLLERINFO + * 21.9 MDACIOCTL_GETLOGDEVINFOVALID + * 21.10 MDACIOCTL_GETPHYSDEVINFOVALID + * 21.11 MDACIOCTL_GETPHYSDEVSTATISTICS + * 21.12 MDACIOCTL_GETLOGDEVSTATISTICS + * 21.13 MDACIOCTL_GETCONTROLLERSTATISTICS + * 21.27 MDACIOCTL_GETBDT_FOR_SYSDRIVE + * 23.4 MDACIOCTL_CREATENEWCONF + * 23.5 MDACIOCTL_ADDNEWCONF + * 23.8 MDACIOCTL_GETDEVCONFINFO + * 23.9 MDACIOCTL_GETFREESPACELIST + * 24.1 MDACIOCTL_MORE + * 25.1 MDACIOCTL_GETPHYSDEVPARAMETER + * 25.2 MDACIOCTL_SETPHYSDEVPARAMETER + * 25.3 MDACIOCTL_GETLOGDEVPARAMETER + * 25.4 MDACIOCTL_SETLOGDEVPARAMETER + * 25.5 MDACIOCTL_GETCONTROLLERPARAMETER + * 25.6 MDACIOCTL_SETCONTROLLERPARAMETER + * + * These commands just transfer data + */ +struct mly_ioctl_param_data { + u_int8_t param[10]; + union mly_cmd_transfer transfer; +} __attribute__ ((__packed__)); + +/* + * 21.2 MDACIOCTL_SETMEMORYMAILBOX + */ +struct mly_ioctl_param_setmemorymailbox { + u_int8_t health_buffer_size; + u_int8_t res1; + u_int64_t health_buffer_physaddr; + u_int64_t command_mailbox_physaddr; + u_int64_t status_mailbox_physaddr; + u_int64_t res2[2]; +} __attribute__ ((__packed__)); + +/* + * 21.8.2 MDACIOCTL_GETCONTROLLERINFO: Data Format + */ +struct mly_ioctl_getcontrollerinfo { + u_int8_t res1; /* N/A */ + u_int8_t interface_type; + u_int8_t controller_type; + u_int8_t res2; /* N/A */ + u_int16_t interface_speed; + u_int8_t interface_width; + u_int8_t res3[9]; /* N/A */ + char interface_name[16]; + char controller_name[16]; + u_int8_t res4[16]; /* N/A */ + /* firmware release information */ + u_int8_t fw_major; + u_int8_t fw_minor; + u_int8_t fw_turn; + u_int8_t fw_build; + u_int8_t fw_day; + u_int8_t fw_month; + u_int8_t fw_century; + u_int8_t fw_year; + /* hardware release information */ + u_int8_t hw_revision; /* N/A */ + u_int8_t res5[3]; /* N/A */ + u_int8_t hw_release_day; /* N/A */ + u_int8_t hw_release_month; /* N/A */ + u_int8_t hw_release_century; /* N/A */ + u_int8_t hw_release_year; /* N/A */ + /* hardware manufacturing information */ + u_int8_t batch_number; /* N/A */ + u_int8_t res6; /* N/A */ + u_int8_t plant_number; + u_int8_t res7; + u_int8_t hw_manuf_day; + u_int8_t hw_manuf_month; + u_int8_t hw_manuf_century; + u_int8_t hw_manuf_year; + u_int8_t max_pdd_per_xldd; + u_int8_t max_ildd_per_xldd; + u_int16_t nvram_size; + u_int8_t max_number_of_xld; /* N/A */ + u_int8_t res8[3]; /* N/A */ + /* unique information per controller */ + char serial_number[16]; + u_int8_t res9[16]; /* N/A */ + /* vendor information */ + u_int8_t res10[3]; /* N/A */ + u_int8_t oem_information; + char vendor_name[16]; /* N/A */ + /* other physical/controller/operation information */ + u_int8_t bbu_present:1; + u_int8_t active_clustering:1; + u_int8_t res11:6; /* N/A */ + u_int8_t res12[3]; /* N/A */ + /* physical device scan information */ + u_int8_t physical_scan_active:1; + u_int8_t res13:7; /* N/A */ + u_int8_t physical_scan_channel; + u_int8_t physical_scan_target; + u_int8_t physical_scan_lun; + /* maximum command data transfer size */ + u_int16_t maximum_block_count; + u_int16_t maximum_sg_entries; + /* logical/physical device counts */ + u_int16_t logical_devices_present; + u_int16_t logical_devices_critical; + u_int16_t logical_devices_offline; + u_int16_t physical_devices_present; + u_int16_t physical_disks_present; + u_int16_t physical_disks_critical; /* N/A */ + u_int16_t physical_disks_offline; + u_int16_t maximum_parallel_commands; + /* channel and target ID information */ + u_int8_t physical_channels_present; + u_int8_t virtual_channels_present; + u_int8_t physical_channels_possible; + u_int8_t virtual_channels_possible; + u_int8_t maximum_targets_possible[16]; /* N/A (6 and up) */ + u_int8_t res14[12]; /* N/A */ + /* memory/cache information */ + u_int16_t memory_size; + u_int16_t cache_size; + u_int32_t valid_cache_size; /* N/A */ + u_int32_t dirty_cache_size; /* N/A */ + u_int16_t memory_speed; + u_int8_t memory_width; + u_int8_t memory_type:5; + u_int8_t res15:1; /* N/A */ + u_int8_t memory_parity:1; + u_int8_t memory_ecc:1; + char memory_information[16]; /* N/A */ + /* execution memory information */ + u_int16_t exmemory_size; + u_int16_t l2cache_size; /* N/A */ + u_int8_t res16[8]; /* N/A */ + u_int16_t exmemory_speed; + u_int8_t exmemory_width; + u_int8_t exmemory_type:5; + u_int8_t res17:1; /* N/A */ + u_int8_t exmemory_parity:1; + u_int8_t exmemory_ecc:1; + char exmemory_name[16]; /* N/A */ + /* CPU information */ + struct { + u_int16_t speed; + u_int8_t type; + u_int8_t number; + u_int8_t res1[12]; /* N/A */ + char name[16]; /* N/A */ + } cpu[2] __attribute__ ((__packed__)); + /* debugging/profiling/command time tracing information */ + u_int16_t profiling_page; /* N/A */ + u_int16_t profiling_programs; /* N/A */ + u_int16_t time_trace_page; /* N/A */ + u_int16_t time_trace_programs; /* N/A */ + u_int8_t res18[8]; /* N/A */ + /* error counters on physical devices */ + u_int16_t physical_device_bus_resets; /* N/A */ + u_int16_t physical_device_parity_errors; /* N/A */ + u_int16_t physical_device_soft_errors; /* N/A */ + u_int16_t physical_device_commands_failed; /* N/A */ + u_int16_t physical_device_miscellaneous_errors; /* N/A */ + u_int16_t physical_device_command_timeouts; /* N/A */ + u_int16_t physical_device_selection_timeouts; /* N/A */ + u_int16_t physical_device_retries; /* N/A */ + u_int16_t physical_device_aborts; /* N/A */ + u_int16_t physical_device_host_command_aborts; /* N/A */ + u_int16_t physical_device_PFAs_detected; /* N/A */ + u_int16_t physical_device_host_commands_failed; /* N/A */ + u_int8_t res19[8]; /* N/A */ + /* error counters on logical devices */ + u_int16_t logical_device_soft_errors; /* N/A */ + u_int16_t logical_device_commands_failed; /* N/A */ + u_int16_t logical_device_host_command_aborts; /* N/A */ + u_int16_t res20; /* N/A */ + /* error counters on controller */ + u_int16_t controller_parity_ecc_errors; + u_int16_t controller_host_command_aborts; /* N/A */ + u_int8_t res21[4]; /* N/A */ + /* long duration activity information */ + u_int16_t background_inits_active; + u_int16_t logical_inits_active; + u_int16_t physical_inits_active; + u_int16_t consistency_checks_active; + u_int16_t rebuilds_active; + u_int16_t MORE_active; + u_int16_t patrol_active; /* N/A */ + u_int8_t long_operation_status; /* N/A */ + u_int8_t res22; /* N/A */ + /* flash ROM information */ + u_int8_t flash_type; /* N/A */ + u_int8_t res23; /* N/A */ + u_int16_t flash_size; + u_int32_t flash_maximum_age; + u_int32_t flash_age; + u_int8_t res24[4]; /* N/A */ + char flash_name[16]; /* N/A */ + /* firmware runtime information */ + u_int8_t rebuild_rate; + u_int8_t background_init_rate; + u_int8_t init_rate; + u_int8_t consistency_check_rate; + u_int8_t res25[4]; /* N/A */ + u_int32_t maximum_dp; + u_int32_t free_dp; + u_int32_t maximum_iop; + u_int32_t free_iop; + u_int16_t maximum_comb_length; + u_int16_t maximum_configuration_groups; + u_int8_t installation_abort:1; + u_int8_t maintenance:1; + u_int8_t res26:6; /* N/A */ + u_int8_t res27[3]; /* N/A */ + u_int8_t res28[32 + 512]; /* N/A */ +} __attribute__ ((__packed__)); + +/* + * 21.9.2 MDACIOCTL_GETLOGDEVINFOVALID + */ +struct mly_ioctl_getlogdevinfovalid { + u_int8_t res1; /* N/A */ + u_int8_t channel; + u_int8_t target; + u_int8_t lun; + u_int8_t state; /* see 8.1 */ + u_int8_t raid_level; /* see 8.2 */ + u_int8_t stripe_size; /* see 8.3 */ + u_int8_t cache_line_size; /* see 8.4 */ + u_int8_t read_write_control; /* see 8.5 */ + u_int8_t consistency_check:1; + u_int8_t rebuild:1; + u_int8_t make_consistent:1; + u_int8_t initialisation:1; + u_int8_t migration:1; + u_int8_t patrol:1; + u_int8_t res2:2; /* N/A */ + u_int8_t ar5_limit; + u_int8_t ar5_algo; + u_int16_t logical_device_number; + u_int16_t bios_control; + /* erorr counters */ + u_int16_t soft_errors; /* N/A */ + u_int16_t commands_failed; /* N/A */ + u_int16_t host_command_aborts; /* N/A */ + u_int16_t deferred_write_errors; /* N/A */ + u_int8_t res3[8]; /* N/A */ + /* device size information */ + u_int8_t res4[2]; /* N/A */ + u_int16_t device_block_size; + u_int32_t original_device_size; /* N/A */ + u_int32_t device_size; /* XXX "blocks or MB" Huh? */ + u_int8_t res5[4]; /* N/A */ + char device_name[32]; /* N/A */ + u_int8_t inquiry[36]; + u_int8_t res6[12]; /* N/A */ + u_int64_t last_read_block; /* N/A */ + u_int64_t last_written_block; /* N/A */ + u_int64_t consistency_check_block; + u_int64_t rebuild_block; + u_int64_t make_consistent_block; + u_int64_t initialisation_block; + u_int64_t migration_block; + u_int64_t patrol_block; /* N/A */ + u_int8_t res7[64]; /* N/A */ +} __attribute__ ((__packed__)); + +/* + * 21.10.2 MDACIOCTL_GETPHYSDEVINFOVALID: Data Format + */ +struct mly_ioctl_getphysdevinfovalid { + u_int8_t res1; + u_int8_t channel; + u_int8_t target; + u_int8_t lun; + u_int8_t raid_ft:1; /* configuration status */ + u_int8_t res2:1; /* N/A */ + u_int8_t local:1; + u_int8_t res3:5; + u_int8_t host_dead:1; /* multiple host/controller status *//* N/A */ + u_int8_t host_connection_dead:1; /* N/A */ + u_int8_t res4:6; /* N/A */ + u_int8_t state; /* see 8.1 */ + u_int8_t width; + u_int16_t speed; + /* multiported physical device information */ + u_int8_t ports_available; /* N/A */ + u_int8_t ports_inuse; /* N/A */ + u_int8_t res5[4]; + u_int8_t ether_address[16]; /* N/A */ + u_int16_t command_tags; + u_int8_t consistency_check:1; /* N/A */ + u_int8_t rebuild:1; /* N/A */ + u_int8_t make_consistent:1; /* N/A */ + u_int8_t initialisation:1; + u_int8_t migration:1; /* N/A */ + u_int8_t patrol:1; /* N/A */ + u_int8_t res6:2; + u_int8_t long_operation_status; /* N/A */ + u_int8_t parity_errors; + u_int8_t soft_errors; + u_int8_t hard_errors; + u_int8_t miscellaneous_errors; + u_int8_t command_timeouts; /* N/A */ + u_int8_t retries; /* N/A */ + u_int8_t aborts; /* N/A */ + u_int8_t PFAs_detected; /* N/A */ + u_int8_t res7[6]; + u_int16_t block_size; + u_int32_t original_device_size; /* XXX "blocks or MB" Huh? */ + u_int32_t device_size; /* XXX "blocks or MB" Huh? */ + u_int8_t res8[4]; + char name[16]; /* N/A */ + u_int8_t res9[16 + 32]; + u_int8_t inquiry[36]; + u_int8_t res10[12 + 16]; + u_int64_t last_read_block; /* N/A */ + u_int64_t last_written_block; /* N/A */ + u_int64_t consistency_check_block; /* N/A */ + u_int64_t rebuild_block; /* N/A */ + u_int64_t make_consistent_block; /* N/A */ + u_int64_t initialisation_block; /* N/A */ + u_int64_t migration_block; /* N/A */ + u_int64_t patrol_block; /* N/A */ + u_int8_t res11[256]; +} __attribute__ ((__packed__)); + +union mly_devinfo { + struct mly_ioctl_getlogdevinfovalid logdev; + struct mly_ioctl_getphysdevinfovalid physdev; +}; + +/* + * 21.11.2 MDACIOCTL_GETPHYSDEVSTATISTICS: Data Format + * 21.12.2 MDACIOCTL_GETLOGDEVSTATISTICS: Data Format + */ +struct mly_ioctl_getdevstatistics { + u_int32_t uptime_ms; /* getphysedevstatistics only */ + u_int8_t res1[5]; /* N/A */ + u_int8_t channel; + u_int8_t target; + u_int8_t lun; + u_int16_t raid_device; /* getlogdevstatistics only */ + u_int8_t res2[2]; /* N/A */ + /* total read/write performance including cache data */ + u_int32_t total_reads; + u_int32_t total_writes; + u_int32_t total_read_size; + u_int32_t total_write_size; + /* cache read/write performance */ + u_int32_t cache_reads; /* N/A */ + u_int32_t cache_writes; /* N/A */ + u_int32_t cache_read_size; /* N/A */ + u_int32_t cache_write_size; /* N/A */ + /* commands active/wait information */ + u_int32_t command_waits_done; /* N/A */ + u_int16_t active_commands; /* N/A */ + u_int16_t waiting_commands; /* N/A */ + u_int8_t res3[8]; /* N/A */ +} __attribute__ ((__packed__)); + +/* + * 21.13.2 MDACIOCTL_GETCONTROLLERSTATISTICS: Data Format + */ +struct mly_ioctl_getcontrollerstatistics { + u_int32_t uptime_ms; /* N/A */ + u_int8_t res1[12]; /* N/A */ + /* target physical device performance data information */ + u_int32_t target_physical_device_interrupts; /* N/A */ + u_int32_t target_physical_device_stray_interrupts;/* N/A */ + u_int8_t res2[8]; /* N/A */ + u_int32_t target_physical_device_reads; /* N/A */ + u_int32_t target_physical_device_writes; /* N/A */ + u_int32_t target_physical_device_read_size; /* N/A */ + u_int32_t target_physical_device_write_size; /* N/A */ + /* host system performance data information */ + u_int32_t host_system_interrupts; /* N/A */ + u_int32_t host_system_stray_interrupts; /* N/A */ + u_int32_t host_system_sent_interrupts; /* N/A */ + u_int8_t res3[4]; /* N/A */ + u_int32_t physical_device_reads; /* N/A */ + u_int32_t physical_device_writes; /* N/A */ + u_int32_t physical_device_read_size; /* N/A */ + u_int32_t physical_device_write_size; /* N/A */ + u_int32_t physical_device_cache_reads; /* N/A */ + u_int32_t physical_device_cache_writes; /* N/A */ + u_int32_t physical_device_cache_read_size; /* N/A */ + u_int32_t physical_device_cache_write_size; /* N/A */ + u_int32_t logical_device_reads; /* N/A */ + u_int32_t logical_device_writes; /* N/A */ + u_int32_t logical_device_read_size; /* N/A */ + u_int32_t logical_device_write_size; /* N/A */ + u_int32_t logical_device_cache_reads; /* N/A */ + u_int32_t logical_device_cache_writes; /* N/A */ + u_int32_t logical_device_cache_read_size; /* N/A */ + u_int32_t logical_device_cache_write_size; /* N/A */ + u_int16_t target_physical_device_commands_active; /* N/A */ + u_int16_t target_physical_device_commands_waiting;/* N/A */ + u_int16_t host_system_commands_active; /* N/A */ + u_int16_t host_system_commands_waiting; /* N/A */ + u_int8_t res4[48 + 64]; /* N/A */ +} __attribute__ ((__packed__)); + +/* + * 21.2 MDACIOCTL_SETRAIDDEVSTATE + */ +struct mly_ioctl_param_setraiddevstate { + u_int8_t state; +} __attribute__ ((__packed__)); + +/* + * 21.27.2 MDACIOCTL_GETBDT_FOR_SYSDRIVE: Data Format + */ +#define MLY_MAX_BDT_ENTRIES 1022 +struct mly_ioctl_getbdt_for_sysdrive { + u_int32_t num_of_bdt_entries; + u_int32_t bad_data_block_address[MLY_MAX_BDT_ENTRIES]; +} __attribute__ ((__packed__)); + +/* + * 22.1 Physical Device Definition (PDD) + */ +struct mly_pdd { + u_int8_t type; /* see 8.2 */ + u_int8_t state; /* see 8.1 */ + u_int16_t raid_device; + u_int32_t device_size; /* XXX "block or MB" Huh? */ + u_int8_t controller; + u_int8_t channel; + u_int8_t target; + u_int8_t lun; + u_int32_t start_address; +} __attribute__ ((__packed__)); + +/* + * 22.2 RAID Device Use Definition (UDD) + */ +struct mly_udd { + u_int8_t res1; + u_int8_t state; /* see 8.1 */ + u_int16_t raid_device; + u_int32_t start_address; +} __attribute__ ((__packed__)); + +/* + * RAID Device Definition (LDD) + */ +struct mly_ldd { + u_int8_t type; /* see 8.2 */ + u_int8_t state; /* see 8.1 */ + u_int16_t raid_device; + u_int32_t device_size; /* XXX "block or MB" Huh? */ + u_int8_t devices_used_count; + u_int8_t stripe_size; /* see 8.3 */ + u_int8_t cache_line_size; /* see 8.4 */ + u_int8_t read_write_control; /* see 8.5 */ + u_int32_t devices_used_size; /* XXX "block or MB" Huh? */ + u_int16_t devices_used[32]; /* XXX actual size of this field unknown! */ +} __attribute__ ((__packed__)); + +/* + * Define a datastructure giving the smallest allocation that will hold + * a PDD, UDD or LDD for MDACIOCTL_GETDEVCONFINFO. + */ +struct mly_devconf_hdr { + u_int8_t type; /* see 8.2 */ + u_int8_t state; /* see 8.1 */ + u_int16_t raid_device; +}; + +union mly_ioctl_devconfinfo { + struct mly_pdd pdd; + struct mly_udd udd; + struct mly_ldd ldd; + struct mly_devconf_hdr hdr; +}; + +/* + * 22.3 MDACIOCTL_RENAMERAIDDEV + * + * XXX this command is listed as transferring data, but does not define the data. + */ +struct mly_ioctl_param_renameraiddev { + u_int8_t new_raid_device; +} __attribute__ ((__packed__)); + +/* + * 23.6.2 MDACIOCTL_XLATEPHYSDEVTORAIDDEV + * + * XXX documentation suggests this format will change + */ +struct mly_ioctl_param_xlatephysdevtoraiddev { + u_int16_t raid_device; + u_int8_t res1[2]; + u_int8_t controller; + u_int8_t channel; + u_int8_t target; + u_int8_t lun; +} __attribute__ ((__packed__)); + +/* + * 23.7 MDACIOCTL_GETGROUPCONFINFO + */ +struct mly_ioctl_param_getgroupconfinfo { + u_int16_t group; + u_int8_t res1[8]; + union mly_cmd_transfer transfer; +} __attribute__ ((__packed__)); + +/* + * 23.9.2 MDACIOCTL_GETFREESPACELIST: Data Format + * + * The controller will populate as much of this structure as is provided, + * or as is required to fully list the free space available. + */ +struct mly_ioctl_getfreespacelist_entry { + u_int16_t raid_device; + u_int8_t res1[6]; + u_int32_t address; /* XXX "blocks or MB" Huh? */ + u_int32_t size; /* XXX "blocks or MB" Huh? */ +} __attribute__ ((__packed__)); + +struct mly_ioctl_getfrespacelist { + u_int16_t returned_entries; + u_int16_t total_entries; + u_int8_t res1[12]; + struct mly_ioctl_getfreespacelist_entry space[0]; /* expand to suit */ +} __attribute__ ((__packed__)); + +/* + * 27.1 MDACIOCTL_GETSUBSYSTEMDATA + * 27.2 MDACIOCTL_SETSUBSYSTEMDATA + * + * PCI controller only supports a limited subset of the possible operations. + * + * XXX where does the status end up? (the command transfers no data) + */ +struct mly_ioctl_param_subsystemdata { + u_int8_t operation:4; +#define MLY_BBU_GETSTATUS 0x00 +#define MLY_BBU_SET_THRESHOLD 0x00 /* minutes in param[0,1] */ + u_int8_t subsystem:4; +#define MLY_SUBSYSTEM_BBU 0x01 + u_int parameter[3]; /* only for SETSUBSYSTEMDATA */ +} __attribute__ ((__packed__)); + +struct mly_ioctl_getsubsystemdata_bbustatus { + u_int16_t current_power; + u_int16_t maximum_power; + u_int16_t power_threshold; + u_int8_t charge_level; + u_int8_t hardware_version; + u_int8_t battery_type; +#define MLY_BBU_TYPE_UNKNOWN 0x00 +#define MLY_BBU_TYPE_NICAD 0x01 +#define MLY_BBU_TYPE_MISSING 0xfe + u_int8_t res1; + u_int8_t operation_status; +#define MLY_BBU_STATUS_NO_SYNC 0x01 +#define MLY_BBU_STATUS_OUT_OF_SYNC 0x02 +#define MLY_BBU_STATUS_FIRST_WARNING 0x04 +#define MLY_BBU_STATUS_SECOND_WARNING 0x08 +#define MLY_BBU_STATUS_RECONDITIONING 0x10 +#define MLY_BBU_STATUS_DISCHARGING 0x20 +#define MLY_BBU_STATUS_FASTCHARGING 0x40 + u_int8_t res2; +} __attribute__ ((__packed__)); + +/* + * 28.9 MDACIOCTL_RESETDEVICE + * 28.10 MDACIOCTL_FLUSHDEVICEDATA + * 28.11 MDACIOCTL_PAUSEDEVICE + * 28.12 MDACIOCTL_UNPAUSEDEVICE + */ +struct mly_ioctl_param_deviceoperation { + u_int8_t operation_device; /* see 14.3 */ +} __attribute__ ((__packed__)); + +/* + * 31.1 Event Data Format + */ +struct mly_event { + u_int32_t sequence_number; + u_int32_t timestamp; + u_int32_t code; + u_int8_t controller; + u_int8_t channel; + u_int8_t target; /* also enclosure */ + u_int8_t lun; /* also enclosure unit */ + u_int8_t res1[4]; + u_int32_t param; + u_int8_t sense[40]; +} __attribute__ ((__packed__)); + +/* + * 31.2 MDACIOCTL_GETEVENT + */ +struct mly_ioctl_param_getevent { + u_int16_t sequence_number_low; + u_int8_t res1[8]; + union mly_cmd_transfer transfer; +} __attribute__ ((__packed__)); + +union mly_ioctl_param { + struct mly_ioctl_param_data data; + struct mly_ioctl_param_setmemorymailbox setmemorymailbox; + struct mly_ioctl_param_setraiddevstate setraiddevstate; + struct mly_ioctl_param_renameraiddev renameraiddev; + struct mly_ioctl_param_xlatephysdevtoraiddev xlatephysdevtoraiddev; + struct mly_ioctl_param_getgroupconfinfo getgroupconfinfo; + struct mly_ioctl_param_subsystemdata subsystemdata; + struct mly_ioctl_param_deviceoperation deviceoperation; + struct mly_ioctl_param_getevent getevent; +}; + +/* + * 19 SCSI Command Format + */ +#define MLY_PHYADDR(c, b, t, l) \ + ((l) | ((t) << 8) | ((b) << 16) | ((c) << 19)) +#define MLY_LOGADDR(c, u) \ + ((u) | ((c) << 19)) + +#define MLY_LOGADDR_DEV(a) (a & 0xffff) +#define MLY_LOGADDR_CTLR(a) (a >> 19) + +#define MLY_PHYADDR_LUN(a) (a & 0xff) +#define MLY_PHYADDR_TARGET(a) ((a >> 8) & 0xff) +#define MLY_PHYADDR_CHANNEL(a) ((a >> 16) & 0x07) +#define MLY_PHYADDR_CTLR(a) ((a >> 19) & 0x1f) + +/* + * struct mly_cmd_address_physical { + * u_int8_t lun; + * u_int8_t target; + * u_int8_t channel:3; + * u_int8_t controller:5; + * } __attribute__ ((__packed__)); + * + * struct mly_cmd_address_logical { + * u_int16_t logdev; + * u_int8_t res1:3; + * u_int8_t controller:5; + * } __attribute__ ((__packed__)); + */ + +struct mly_cmd_generic { + u_int16_t command_id; + u_int8_t opcode; + u_int8_t command_control; + u_int32_t data_size; + u_int64_t sense_buffer_address; + u_int8_t addr[3]; + u_int8_t timeout; + u_int8_t maximum_sense_size; + u_int8_t res1[11]; + union mly_cmd_transfer transfer; +} __attribute__ ((__packed__)); + + +/* + * 19.1 MDACMD_SCSI & MDACMD_SCSIPT + */ +#define MLY_CMD_SCSI_SMALL_CDB 10 +struct mly_cmd_scsi_small { + u_int16_t command_id; + u_int8_t opcode; + u_int8_t command_control; + u_int32_t data_size; + u_int64_t sense_buffer_address; + u_int8_t addr[3]; + u_int8_t timeout; + u_int8_t maximum_sense_size; + u_int8_t cdb_length; + u_int8_t cdb[MLY_CMD_SCSI_SMALL_CDB]; + union mly_cmd_transfer transfer; +} __attribute__ ((__packed__)); + +/* + * 19.2 MDACMD_SCSILC & MDACMD_SCSILCPT + */ +struct mly_cmd_scsi_large { + u_int16_t command_id; + u_int8_t opcode; + u_int8_t command_control; + u_int32_t data_size; + u_int64_t sense_buffer_address; + u_int8_t addr[3]; + u_int8_t timeout; + u_int8_t maximum_sense_size; + u_int8_t cdb_length; + u_int16_t res1; + u_int64_t cdb_physaddr; + union mly_cmd_transfer transfer; +} __attribute__ ((__packed__)); + +/* + * 20.1 IOCTL Command Format: Internal Bus + */ +struct mly_cmd_ioctl { + u_int16_t command_id; + u_int8_t opcode; + u_int8_t command_control; + u_int32_t data_size; + u_int64_t sense_buffer_address; + u_int8_t addr[3]; + u_int8_t timeout; + u_int8_t maximum_sense_size; + u_int8_t sub_ioctl; + union mly_ioctl_param param; +} __attribute__ ((__packed__)); + +/* + * PG6: 8.2.2 + */ +struct mly_cmd_mmbox { + u_int32_t flag; + u_int8_t data[60]; +} __attribute__ ((__packed__)); + +union mly_cmd_packet { + struct mly_cmd_generic generic; + struct mly_cmd_scsi_small scsi_small; + struct mly_cmd_scsi_large scsi_large; + struct mly_cmd_ioctl ioctl; + struct mly_cmd_mmbox mmbox; +}; + +/* + * PG6: 5.3 + */ +#define MLY_I960RX_COMMAND_MAILBOX 0x10 +#define MLY_I960RX_STATUS_MAILBOX 0x18 +#define MLY_I960RX_IDBR 0x20 +#define MLY_I960RX_ODBR 0x2c +#define MLY_I960RX_ERROR_STATUS 0x2e +#define MLY_I960RX_INTERRUPT_STATUS 0x30 +#define MLY_I960RX_INTERRUPT_MASK 0x34 + +#define MLY_STRONGARM_COMMAND_MAILBOX 0x50 +#define MLY_STRONGARM_STATUS_MAILBOX 0x58 +#define MLY_STRONGARM_IDBR 0x60 +#define MLY_STRONGARM_ODBR 0x61 +#define MLY_STRONGARM_ERROR_STATUS 0x63 +#define MLY_STRONGARM_INTERRUPT_STATUS 0x30 +#define MLY_STRONGARM_INTERRUPT_MASK 0x34 + +/* + * PG6: 5.4.3 Doorbell 0 + */ +#define MLY_HM_CMDSENT (1<<0) +#define MLY_HM_STSACK (1<<1) +#define MLY_SOFT_RST (1<<3) +#define MLY_AM_CMDSENT (1<<4) + +/* + * PG6: 5.4.4 Doorbell 1 + * + * Note that the documentation claims that these bits are set when the + * status queue(s) are empty, wheras the Linux driver and experience + * suggest they are set when there is status available. + */ +#define MLY_HM_STSREADY (1<<0) +#define MLY_AM_STSREADY (1<<1) + +/* + * PG6: 5.4.6 Doorbell 3 + */ +#define MLY_MSG_EMPTY (1<<3) +#define MLY_MSG_SPINUP 0x08 +#define MLY_MSG_RACE_RECOVERY_FAIL 0x60 +#define MLY_MSG_RACE_IN_PROGRESS 0x70 +#define MLY_MSG_RACE_ON_CRITICAL 0xb0 +#define MLY_MSG_PARITY_ERROR 0xf0 + +/* + * PG6: 5.4.8 Outbound Interrupt Mask + */ +#define MLY_INTERRUPT_MASK_DISABLE 0xff +#define MLY_INTERRUPT_MASK_ENABLE (0xff & ~(1<<2)) + +/* + * PG6: 8.2 Advanced Mailbox Scheme + * + * Note that this must be allocated on a 4k boundary, and all internal + * fields must also reside on a 4k boundary. + * We could dynamically size this structure, but the extra effort + * is probably unjustified. Note that these buffers do not need to be + * adjacent - we just group them to simplify allocation of the bus-visible + * buffer. + * + * XXX Note that for some reason, if MLY_MMBOX_COMMANDS is > 64, the controller + * fails to respond to the command at (MLY_MMBOX_COMMANDS - 64). It's not + * wrapping to 0 at this point (determined by experimentation). This is not + * consistent with the Linux driver's implementation. + * Whilst it's handy to have lots of room for status returns in case we end up + * being slow getting back to completed commands, it seems unlikely that we + * would get 64 commands ahead of the controller on the submissions side, so + * the current workaround is to simply limit the command ring to 64 entries. + */ +union mly_status_packet { + struct mly_status status; + struct { + u_int32_t flag; + u_int8_t data[4]; + } __attribute__ ((__packed__)) mmbox; +}; +union mly_health_region { + struct mly_health_status status; + u_int8_t pad[1024]; +}; + +#define MLY_MMBOX_COMMANDS 64 +#define MLY_MMBOX_STATUS 512 +struct mly_mmbox { + union mly_cmd_packet mmm_command[MLY_MMBOX_COMMANDS]; + union mly_status_packet mmm_status[MLY_MMBOX_STATUS]; + union mly_health_region mmm_health; +} __attribute__ ((__packed__)); + +#endif /* !defined _PCI_MLYREG_H_ */ diff --git a/sys/dev/pci/mlyvar.h b/sys/dev/pci/mlyvar.h new file mode 100644 index 00000000000..4be8394604f --- /dev/null +++ b/sys/dev/pci/mlyvar.h @@ -0,0 +1,345 @@ +/* $NetBSD: mlyvar.h,v 1.1 2001/07/30 19:59:07 ad Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Andrew Doran, Thor Lancelot Simon, and Eric Haszlakiewicz. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/*- + * Copyright (c) 2000, 2001 Michael Smith + * Copyright (c) 2000 BSDi + * 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. + * + * from FreeBSD: mlyvar.h,v 1.3 2001/07/14 00:12:22 msmith Exp + */ + +#ifndef _PCI_MLYVAR_H_ +#define _PCI_MLYVAR_H_ + +/* + * The firmware interface allows for a 16-bit command identifier. We cap + * ourselves at a reasonable limit. Note that we reserve a small number of + * CCBs for control operations. + */ +#define MLY_MAX_CCBS 256 +#define MLY_CCBS_RESV 4 + +/* + * The firmware interface allows for a 16-bit s/g list length. We limit + * ourselves to a reasonable maximum. + */ +#define MLY_MAX_SEGS 17 +#define MLY_SGL_SIZE (MLY_MAX_SEGS * sizeof(struct mly_sg_entry)) + +#define MLY_MAX_XFER ((MLY_MAX_SEGS - 1) * PAGE_SIZE) + +/* + * The interval at which we poke the controller for status updates (in + * seconds). + */ +#define MLY_PERIODIC_INTERVAL 5 + +/* + * Command slot regulation. We can't use slot 0 due to the memory mailbox + * implementation. + */ +#define MLY_SLOT_START 1 +#define MLY_SLOT_MAX (MLY_SLOT_START + MLY_MAX_CCBS) + +/* + * Per-device structure, used to save persistent state on devices. + * + * Note that this isn't really Bus/Target/Lun since we don't support lun != + * 0 at this time. + */ +struct mly_btl { + int mb_flags; + int mb_state; /* See 8.1 */ + int mb_type; /* See 8.2 */ + + /* Physical devices only. */ + int mb_speed; /* Interface transfer rate */ + int mb_width; /* Interface width */ +}; +#define MLY_BTL_PHYSICAL 0x01 /* physical device */ +#define MLY_BTL_LOGICAL 0x02 /* logical device */ +#define MLY_BTL_PROTECTED 0x04 /* I/O not allowed */ +#define MLY_BTL_TQING 0x08 /* tagged queueing */ +#define MLY_BTL_SCANNING 0x10 /* scan in progress */ +#define MLY_BTL_RESCAN 0x20 /* need to re-scan */ + +/* + * Per-command context. + */ +struct mly_softc; + +struct mly_ccb { + union { + SLIST_ENTRY(mly_ccb) slist; + SIMPLEQ_ENTRY(mly_ccb) simpleq; + } mc_link; /* list linkage */ + + u_int mc_slot; /* command slot we occupy */ + u_int mc_flags; /* status flags */ + u_int mc_status; /* command completion status */ + u_int mc_sense; /* sense data length */ + int32_t mc_resid; /* I/O residual count */ + + union mly_cmd_packet *mc_packet;/* our controller command */ + bus_addr_t mc_packetphys; /* physical address of the mapped packet */ + + void *mc_data; /* data buffer */ + size_t mc_length; /* data length */ + bus_dmamap_t mc_datamap; /* DMA map for data */ + u_int mc_sgoff; /* S/G list offset */ + + void (*mc_complete)(struct mly_softc *, struct mly_ccb *); + void *mc_private; +}; +#define MLY_CCB_DATAIN 0x01 +#define MLY_CCB_DATAOUT 0x02 +#define MLY_CCB_MAPPED 0x04 +#define MLY_CCB_COMPLETE 0x08 + +/* + * Per-controller context. + */ +struct mly_softc { + /* Generic device info. */ + struct device mly_dv; + bus_space_handle_t mly_ioh; + bus_space_tag_t mly_iot; + bus_dma_tag_t mly_dmat; + void *mly_ih; + + /* Scatter-gather lists. */ + struct mly_sg_entry *mly_sg; + bus_addr_t mly_sg_busaddr; + bus_dma_tag_t mly_sg_dmat; + bus_dmamap_t mly_sg_dmamap; + bus_dma_segment_t mly_sg_seg; + + /* Memory mailbox. */ + struct mly_mmbox *mly_mmbox; + bus_addr_t mly_mmbox_busaddr; + bus_dma_tag_t mly_mmbox_dmat; + bus_dmamap_t mly_mmbox_dmamap; + bus_dma_segment_t mly_mmbox_seg; + u_int mly_mmbox_cmd_idx; + u_int mly_mmbox_sts_idx; + + /* Command packets. */ + union mly_cmd_packet *mly_pkt; + bus_addr_t mly_pkt_busaddr; + bus_dma_tag_t mly_pkt_dmat; + bus_dmamap_t mly_pkt_dmamap; + bus_dma_segment_t mly_pkt_seg; + + /* Command management. */ + struct mly_ccb *mly_ccbs; + SLIST_HEAD(,mly_ccb) mly_ccb_free; + SIMPLEQ_HEAD(,mly_ccb) mly_ccb_queue; + u_int mly_ncmds; + + /* Controller hardware interface. */ + u_int mly_hwif; + u_int mly_doorbell_true; + u_int mly_cmd_mailbox; + u_int mly_status_mailbox; + u_int mly_idbr; + u_int mly_odbr; + u_int mly_error_status; + u_int mly_interrupt_status; + u_int mly_interrupt_mask; + + /* Controller features, limits and status. */ + u_int mly_state; + struct mly_ioctl_getcontrollerinfo *mly_controllerinfo; + struct mly_param_controller *mly_controllerparam; + struct mly_btl mly_btl[MLY_MAX_CHANNELS][MLY_MAX_TARGETS]; + + /* Health monitoring. */ + u_int mly_event_change; + u_int mly_event_counter; + u_int mly_event_waiting; + struct proc *mly_thread; + + /* SCSI mid-layer connection. */ + struct scsipi_adapter mly_adapt; + struct scsipi_channel mly_chans[MLY_MAX_CHANNELS]; + u_int mly_nchans; +}; +#define MLY_HWIF_I960RX 0 +#define MLY_HWIF_STRONGARM 1 + +#define MLY_STATE_OPEN 0x01 +#define MLY_STATE_MMBOX_ACTIVE 0x02 +#define MLY_STATE_INITOK 0x04 + +/* + * Register access helpers. + */ + +static __inline__ u_int8_t mly_inb(struct mly_softc *, int); +static __inline__ u_int16_t mly_inw(struct mly_softc *, int); +static __inline__ u_int32_t mly_inl(struct mly_softc *, int); +static __inline__ void mly_outb(struct mly_softc *, int, u_int8_t); +static __inline__ void mly_outw(struct mly_softc *, int, u_int16_t); +static __inline__ void mly_outl(struct mly_softc *, int, u_int32_t); +static __inline__ int mly_idbr_true(struct mly_softc *, u_int8_t); +static __inline__ int mly_odbr_true(struct mly_softc *, u_int8_t); +static __inline__ int mly_error_valid(struct mly_softc *); + +static __inline__ u_int8_t +mly_inb(struct mly_softc *mly, int off) +{ + + bus_space_barrier(mly->mly_iot, mly->mly_ioh, off, 1, + BUS_SPACE_BARRIER_WRITE | BUS_SPACE_BARRIER_READ); + return (bus_space_read_1(mly->mly_iot, mly->mly_ioh, off)); +} + +static __inline__ u_int16_t +mly_inw(struct mly_softc *mly, int off) +{ + + bus_space_barrier(mly->mly_iot, mly->mly_ioh, off, 2, + BUS_SPACE_BARRIER_WRITE | BUS_SPACE_BARRIER_READ); + return (bus_space_read_2(mly->mly_iot, mly->mly_ioh, off)); +} + +static __inline__ u_int32_t +mly_inl(struct mly_softc *mly, int off) +{ + + bus_space_barrier(mly->mly_iot, mly->mly_ioh, off, 4, + BUS_SPACE_BARRIER_WRITE | BUS_SPACE_BARRIER_READ); + return (bus_space_read_4(mly->mly_iot, mly->mly_ioh, off)); +} + +static __inline__ void +mly_outb(struct mly_softc *mly, int off, u_int8_t val) +{ + + bus_space_write_1(mly->mly_iot, mly->mly_ioh, off, val); + bus_space_barrier(mly->mly_iot, mly->mly_ioh, off, 1, + BUS_SPACE_BARRIER_WRITE); +} + +static __inline__ void +mly_outw(struct mly_softc *mly, int off, u_int16_t val) +{ + + bus_space_write_2(mly->mly_iot, mly->mly_ioh, off, val); + bus_space_barrier(mly->mly_iot, mly->mly_ioh, off, 2, + BUS_SPACE_BARRIER_WRITE); +} + +static __inline__ void +mly_outl(struct mly_softc *mly, int off, u_int32_t val) +{ + + bus_space_write_4(mly->mly_iot, mly->mly_ioh, off, val); + bus_space_barrier(mly->mly_iot, mly->mly_ioh, off, 4, + BUS_SPACE_BARRIER_WRITE); +} + +static __inline__ int +mly_idbr_true(struct mly_softc *mly, u_int8_t mask) +{ + u_int8_t val; + + val = mly_inb(mly, mly->mly_idbr) ^ mly->mly_doorbell_true; + return ((val & mask) == mask); +} + +static __inline__ int +mly_odbr_true(struct mly_softc *mly, u_int8_t mask) +{ + + return ((mly_inb(mly, mly->mly_odbr) & mask) == mask); +} + +static __inline__ int +mly_error_valid(struct mly_softc *mly) +{ + u_int8_t val; + + val = mly_inb(mly, mly->mly_error_status) ^ mly->mly_doorbell_true; + return ((val & MLY_MSG_EMPTY) == 0); +} + +/* + * Bus/target/logical ID-related macros. + */ + +#define MLY_LOGDEV_ID(mly, bus, target) \ + (((bus) - (mly)->mly_controllerinfo->physical_channels_present) * \ + MLY_MAX_TARGETS + (target)) + +#define MLY_LOGDEV_BUS(mly, logdev) \ + (((logdev) / MLY_MAX_TARGETS) + \ + (mly)->mly_controllerinfo->physical_channels_present) + +#define MLY_LOGDEV_TARGET(mly, logdev) \ + ((logdev) % MLY_MAX_TARGETS) + +#define MLY_BUS_IS_VIRTUAL(mly, bus) \ + ((bus) >= (mly)->mly_controllerinfo->physical_channels_present) + +#define MLY_BUS_IS_VALID(mly, bus) \ + (((bus) < (mly)->mly_nchans)) + +#endif /* !defined _PCI_MLYVAR_H_ */ |
