summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjlemon <jlemon@FreeBSD.org>1999-06-24 03:33:30 +0000
committerjlemon <jlemon@FreeBSD.org>1999-06-24 03:33:30 +0000
commit362176b2d08815d30336ee553c6552fbbbdc86fa (patch)
treee82681dce51ee4bc1bbb2f1a42462cb558c2e4a9
parente7aad4d0411fc35acc35206618409c8338a18772 (diff)
downloadFreeBSD-src-362176b2d08815d30336ee553c6552fbbbdc86fa.zip
FreeBSD-src-362176b2d08815d30336ee553c6552fbbbdc86fa.tar.gz
Compaq Smart RAID driver for -current. Based on the original ida.c
driver by Mark Dawson. This probably needs some work, but is stable enough to boot a RAID-only configuration, and survive `make world'.
-rw-r--r--sys/dev/ida/ida.c512
-rw-r--r--sys/dev/ida/ida_disk.c331
-rw-r--r--sys/dev/ida/ida_pci.c199
-rw-r--r--sys/dev/ida/idareg.h99
-rw-r--r--sys/dev/ida/idavar.h166
-rw-r--r--sys/i386/isa/ida.c1786
-rw-r--r--sys/pci/ida_pci.c199
7 files changed, 1506 insertions, 1786 deletions
diff --git a/sys/dev/ida/ida.c b/sys/dev/ida/ida.c
new file mode 100644
index 0000000..00fbc27
--- /dev/null
+++ b/sys/dev/ida/ida.c
@@ -0,0 +1,512 @@
+/*-
+ * Copyright (c) 1999 Jonathan Lemon
+ * All rights reserved.
+ *
+ # Derived from the original IDA Compaq RAID driver, which is
+ * Copyright (c) 1996, 1997, 1998, 1999
+ * Mark Dawson and David James. 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.
+ *
+ * $Id$
+ */
+
+/*
+ * Generic driver for Compaq SMART RAID adapters.
+ *
+ * Specific probe routines are in:
+ * pci/ida_pci.c
+ * i386/eisa/ida_eisa.c
+ */
+
+#include <pci.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/buf.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/devicestat.h>
+
+#if NPCI > 0
+#include <machine/bus_memio.h>
+#endif
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <machine/clock.h>
+#include <sys/rman.h>
+
+#include <dev/ida/idareg.h>
+#include <dev/ida/idavar.h>
+
+#define ida_inl(ida, port) \
+ bus_space_read_4((ida)->tag, (ida)->bsh, port)
+
+#define ida_outl(ida, port, val) \
+ bus_space_write_4((ida)->tag, (ida)->bsh, port, val)
+
+/* prototypes */
+static void ida_alloc_qcb(struct ida_softc *ida);
+static void ida_construct_qcb(struct ida_softc *ida);
+static void ida_start(struct ida_softc *ida);
+static void ida_done(struct ida_softc *ida, struct ida_qcb *qcb);
+
+void
+ida_free(struct ida_softc *ida)
+{
+
+ /*
+ * still need to call bus_dmamap_destroy() for each map created
+ * in ida_alloc_qcb().
+ */
+
+ if (ida->hwqcb_busaddr)
+ bus_dmamap_unload(ida->hwqcb_dmat, ida->hwqcb_dmamap);
+
+ if (ida->hwqcbs)
+ bus_dmamem_free(ida->hwqcb_dmat, ida->hwqcbs,
+ ida->hwqcb_dmamap);
+
+ if (ida->buffer_dmat)
+ bus_dma_tag_destroy(ida->buffer_dmat);
+
+ if (ida->hwqcb_dmat)
+ bus_dma_tag_destroy(ida->hwqcb_dmat);
+
+ if (ida->qcbs != NULL)
+ free(ida->qcbs, M_DEVBUF);
+
+ if (ida->ih != NULL)
+ bus_teardown_intr(ida->dev, ida->irq, ida->ih);
+
+ if (ida->irq != NULL)
+ bus_release_resource(ida->dev, ida->irq_res_type,
+ 0, ida->irq);
+
+ if (ida->parent_dmat != NULL)
+ bus_dma_tag_destroy(ida->parent_dmat);
+
+ if (ida->regs != NULL)
+ bus_release_resource(ida->dev, ida->regs_res_type,
+ ida->regs_res_id, ida->regs);
+}
+
+/*
+ * record bus address from bus_dmamap_load
+ */
+static void
+ida_dma_map_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ bus_addr_t *baddr;
+
+ baddr = (bus_addr_t *)arg;
+ *baddr = segs->ds_addr;
+}
+
+static __inline struct ida_qcb *
+ida_get_qcb(struct ida_softc *ida)
+{
+ struct ida_qcb *qcb;
+
+ if ((qcb = SLIST_FIRST(&ida->free_qcbs)) != NULL) {
+ SLIST_REMOVE_HEAD(&ida->free_qcbs, link.sle);
+ } else {
+ ida_alloc_qcb(ida);
+ if ((qcb = SLIST_FIRST(&ida->free_qcbs)) != NULL)
+ SLIST_REMOVE_HEAD(&ida->free_qcbs, link.sle);
+ }
+ return (qcb);
+}
+
+/*
+ * XXX
+ * since we allocate all QCB space up front during initialization, then
+ * why bother with this routine?
+ */
+static void
+ida_alloc_qcb(struct ida_softc *ida)
+{
+ struct ida_qcb *qcb;
+ int error;
+
+ if (ida->num_qcbs >= IDA_QCB_MAX)
+ return;
+
+ qcb = &ida->qcbs[ida->num_qcbs];
+
+ error = bus_dmamap_create(ida->buffer_dmat, /*flags*/0, &qcb->dmamap);
+ if (error != 0)
+ return;
+
+ qcb->flags = QCB_FREE;
+ qcb->hwqcb = &ida->hwqcbs[ida->num_qcbs];
+ qcb->hwqcb->qcb = qcb;
+ SLIST_INSERT_HEAD(&ida->free_qcbs, qcb, link.sle);
+ ida->num_qcbs++;
+}
+
+int
+ida_init(struct ida_softc *ida)
+{
+ int error;
+
+ ida->unit = device_get_unit(ida->dev);
+ ida->tag = rman_get_bustag(ida->regs);
+ ida->bsh = rman_get_bushandle(ida->regs);
+
+ SLIST_INIT(&ida->free_qcbs);
+ STAILQ_INIT(&ida->qcb_queue);
+ bufq_init(&ida->buf_queue);
+
+ ida->qcbs = (struct ida_qcb *)
+ malloc(IDA_QCB_MAX * sizeof(struct ida_qcb), M_DEVBUF, M_NOWAIT);
+ if (ida->qcbs == NULL)
+ return (ENOMEM);
+ bzero(ida->qcbs, IDA_QCB_MAX * sizeof(struct ida_qcb));
+
+ /*
+ * Create our DMA tags
+ */
+
+ /* DMA tag for our hardware QCB structures */
+ error = bus_dma_tag_create(ida->parent_dmat,
+ /*alignment*/0, /*boundary*/0,
+ /*lowaddr*/BUS_SPACE_MAXADDR, /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ IDA_QCB_MAX * sizeof(struct ida_hardware_qcb),
+ /*nsegments*/1, /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/0, &ida->hwqcb_dmat);
+ if (error)
+ return (ENOMEM);
+
+ /* DMA tag for mapping buffers into device space */
+ error = bus_dma_tag_create(ida->parent_dmat,
+ /*alignment*/0, /*boundary*/0,
+ /*lowaddr*/BUS_SPACE_MAXADDR, /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/MAXBSIZE, /*nsegments*/IDA_NSEG,
+ /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT, /*flags*/0, &ida->buffer_dmat);
+ if (error)
+ return (ENOMEM);
+
+ /* Allocation of hardware QCBs */
+ /* XXX allocation is rounded to hardware page size */
+ error = bus_dmamem_alloc(ida->hwqcb_dmat,
+ (void **)&ida->hwqcbs, BUS_DMA_NOWAIT, &ida->hwqcb_dmamap);
+ if (error)
+ return (ENOMEM);
+
+ /* And permanently map them in */
+ bus_dmamap_load(ida->hwqcb_dmat, ida->hwqcb_dmamap,
+ ida->hwqcbs, IDA_QCB_MAX * sizeof(struct ida_hardware_qcb),
+ ida_dma_map_cb, &ida->hwqcb_busaddr, /*flags*/0);
+
+ bzero(ida->hwqcbs, IDA_QCB_MAX * sizeof(struct ida_hardware_qcb));
+
+ ida_alloc_qcb(ida); /* allocate an initial qcb */
+
+ return (0);
+}
+
+void
+ida_attach(struct ida_softc *ida)
+{
+ struct ida_controller_info cinfo;
+ int error, i;
+
+ ida_outl(ida, R_INT_MASK, INT_DISABLE);
+
+ error = ida_command(ida, CMD_GET_CTRL_INFO, &cinfo, sizeof(cinfo),
+ IDA_CONTROLLER, DMA_DATA_IN);
+ if (error) {
+ device_printf(ida->dev, "CMD_GET_CTRL_INFO failed.\n");
+ return;
+ }
+
+ device_printf(ida->dev, "drives=%d firm_rev=%c%c%c%c\n",
+ cinfo.num_drvs, cinfo.firm_rev[0], cinfo.firm_rev[1],
+ cinfo.firm_rev[2], cinfo.firm_rev[3]);
+
+ ida->num_drives = cinfo.num_drvs;
+
+ for (i = 0; i < ida->num_drives; i++)
+ device_add_child(ida->dev, "id", i, NULL);
+
+ bus_generic_attach(ida->dev);
+
+ ida_outl(ida, R_INT_MASK, INT_ENABLE);
+}
+
+static void
+ida_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
+{
+ struct ida_hardware_qcb *hwqcb = (struct ida_hardware_qcb *)arg;
+ int i;
+
+ hwqcb->hdr.size = (sizeof(struct ida_req) +
+ sizeof(struct ida_sgb) * IDA_NSEG) >> 2;
+
+ for (i = 0; i < nsegments; i++) {
+ hwqcb->seg[i].addr = segs[i].ds_addr;
+ hwqcb->seg[i].length = segs[i].ds_len;
+ }
+ hwqcb->req.sgcount = nsegments;
+}
+
+int
+ida_command(struct ida_softc *ida, int command, void *data, int datasize,
+ int drive, int flags)
+{
+ struct ida_hardware_qcb *hwqcb;
+ struct ida_qcb *qcb;
+ bus_dmasync_op_t op;
+ int s;
+
+ s = splbio();
+ qcb = ida_get_qcb(ida);
+ splx(s);
+
+ if (qcb == NULL) {
+ printf("ida_command: out of QCBs");
+ return (1);
+ }
+
+ hwqcb = qcb->hwqcb;
+ bzero(hwqcb, sizeof(struct ida_hdr) + sizeof(struct ida_req));
+
+ bus_dmamap_load(ida->buffer_dmat, qcb->dmamap,
+ (void *)data, datasize, ida_setup_dmamap, hwqcb, 0);
+ op = qcb->flags & DMA_DATA_IN ?
+ BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE;
+ bus_dmamap_sync(ida->buffer_dmat, qcb->dmamap, op);
+
+ hwqcb->hdr.drive = drive; /* XXX */
+ hwqcb->req.bcount = howmany(datasize, DEV_BSIZE);
+ hwqcb->req.command = command;
+
+ qcb->flags = flags | IDA_COMMAND;
+
+ s = splbio();
+ STAILQ_INSERT_TAIL(&ida->qcb_queue, qcb, link.stqe);
+ ida_start(ida);
+ ida_wait(ida, qcb, 500);
+ splx(s);
+
+ /* XXX should have status returned here? */
+ /* XXX have "status pointer" area in QCB? */
+
+ return (0);
+}
+
+void
+ida_submit_buf(struct ida_softc *ida, struct buf *bp)
+{
+ bufq_insert_tail(&ida->buf_queue, bp);
+ ida_construct_qcb(ida);
+ ida_start(ida);
+}
+
+static void
+ida_construct_qcb(struct ida_softc *ida)
+{
+ struct ida_hardware_qcb *hwqcb;
+ struct ida_qcb *qcb;
+ bus_dmasync_op_t op;
+ struct buf *bp;
+
+ bp = bufq_first(&ida->buf_queue);
+ if (bp == NULL)
+ return; /* no more buffers */
+
+ qcb = ida_get_qcb(ida);
+ if (qcb == NULL)
+ return; /* out of resources */
+
+ bufq_remove(&ida->buf_queue, bp);
+ qcb->buf = bp;
+ qcb->flags = 0;
+
+ hwqcb = qcb->hwqcb;
+ bzero(hwqcb, sizeof(struct ida_hdr) + sizeof(struct ida_req));
+
+ bus_dmamap_load(ida->buffer_dmat, qcb->dmamap,
+ (void *)bp->b_data, bp->b_bcount, ida_setup_dmamap, hwqcb, 0);
+ op = qcb->flags & DMA_DATA_IN ?
+ BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE;
+ bus_dmamap_sync(ida->buffer_dmat, qcb->dmamap, op);
+
+ /*
+ * XXX
+ */
+ {
+ struct id_softc *drv = (struct id_softc *)bp->b_driver1;
+ hwqcb->hdr.drive = drv->unit;
+ }
+
+ hwqcb->req.blkno = bp->b_pblkno;
+ hwqcb->req.bcount = howmany(bp->b_bcount, DEV_BSIZE);
+ hwqcb->req.command = bp->b_flags & B_READ ? CMD_READ : CMD_WRITE;
+
+ STAILQ_INSERT_TAIL(&ida->qcb_queue, qcb, link.stqe);
+}
+
+static __inline bus_addr_t
+idahwqcbvtop(struct ida_softc *ida, struct ida_hardware_qcb *hwqcb)
+{
+ return (ida->hwqcb_busaddr +
+ ((bus_addr_t)hwqcb - (bus_addr_t)ida->hwqcbs));
+}
+
+static __inline struct ida_qcb *
+idahwqcbptov(struct ida_softc *ida, bus_addr_t hwqcb_addr)
+{
+ struct ida_hardware_qcb *hwqcb;
+
+ hwqcb = (struct ida_hardware_qcb *)
+ ((bus_addr_t)ida->hwqcbs + (hwqcb_addr - ida->hwqcb_busaddr));
+ return (hwqcb->qcb);
+}
+
+/*
+ * This routine will be called from ida_intr in order to queue up more
+ * I/O, meaning that we may be in an interrupt context. Hence, we should
+ * not muck around with spl() in this routine.
+ */
+static void
+ida_start(struct ida_softc *ida)
+{
+ struct ida_qcb *qcb;
+
+ while ((qcb = STAILQ_FIRST(&ida->qcb_queue)) != NULL) {
+ if (ida_inl(ida, R_CMD_FIFO) == 0)
+ break; /* fifo is full */
+ STAILQ_REMOVE_HEAD(&ida->qcb_queue, link.stqe);
+ /*
+ * XXX
+ * place the qcb on an active list and set a timeout?
+ */
+ qcb->state = QCB_ACTIVE;
+ /*
+ * XXX
+ * cache the physaddr so we don't keep doing this?
+ */
+ ida_outl(ida, R_CMD_FIFO, idahwqcbvtop(ida, qcb->hwqcb));
+ }
+}
+
+void
+ida_wait(struct ida_softc *ida, struct ida_qcb *qcb, int delay)
+{
+ struct ida_qcb *qcb_done = NULL;
+ bus_addr_t completed;
+
+ if (ida->flags & IDA_ATTACHED) {
+ if (tsleep((caddr_t)qcb, PRIBIO, "idacmd", delay))
+ panic("ida_command: timeout waiting for interrupt");
+ return;
+ }
+
+ while ((completed = ida_inl(ida, R_DONE_FIFO)) == 0) {
+ if (delay-- == 0)
+ panic("ida_wait: timeout waiting for completion");
+ DELAY(10);
+ }
+
+ qcb_done = idahwqcbptov(ida, completed & ~3);
+ if (qcb_done != qcb)
+ panic("ida_wait: incorrect qcb returned");
+ ida_done(ida, qcb);
+ return;
+}
+
+void
+ida_intr(void *data)
+{
+ struct ida_softc *ida;
+ struct ida_qcb *qcb;
+ bus_addr_t completed;
+
+ ida = (struct ida_softc *)data;
+
+ if (ida_inl(ida, R_INT_PENDING) == 0)
+ return; /* not our interrupt */
+
+ while ((completed = ida_inl(ida, R_DONE_FIFO)) != 0) {
+ qcb = idahwqcbptov(ida, completed & ~3);
+
+ if (qcb == NULL || qcb->state != QCB_ACTIVE) {
+ device_printf(ida->dev,
+ "ignoring completion %x\n", completed);
+ continue;
+ }
+ ida_done(ida, qcb);
+ }
+ ida_start(ida);
+}
+
+/*
+ * should switch out command type; may be status, not just I/O.
+ */
+static void
+ida_done(struct ida_softc *ida, struct ida_qcb *qcb)
+{
+ int error = 0;
+
+ /*
+ * finish up command
+ */
+ if (qcb->flags & DMA_DATA_TRANSFER) {
+ bus_dmasync_op_t op;
+
+ op = qcb->flags & DMA_DATA_IN ?
+ BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE;
+ bus_dmamap_sync(ida->buffer_dmat, qcb->dmamap, op);
+ bus_dmamap_unload(ida->buffer_dmat, qcb->dmamap);
+ }
+
+ if (qcb->hwqcb->req.error & SOFT_ERROR)
+ device_printf(ida->dev, "soft error\n");
+ if (qcb->hwqcb->req.error & HARD_ERROR) {
+ error = 1;
+ device_printf(ida->dev, "hard error\n");
+ }
+ if (qcb->hwqcb->req.error & CMD_REJECTED) {
+ error = 1;
+ device_printf(ida->dev, "invalid request\n");
+ }
+
+ if (qcb->flags & IDA_COMMAND) {
+ if (ida->flags & IDA_ATTACHED)
+ wakeup(qcb);
+ } else {
+ if (error)
+ qcb->buf->b_flags |= B_ERROR;
+ id_intr(qcb->buf);
+ }
+
+ qcb->state = QCB_FREE;
+ SLIST_INSERT_HEAD(&ida->free_qcbs, qcb, link.sle);
+ ida_construct_qcb(ida);
+}
diff --git a/sys/dev/ida/ida_disk.c b/sys/dev/ida/ida_disk.c
new file mode 100644
index 0000000..c3c94f5
--- /dev/null
+++ b/sys/dev/ida/ida_disk.c
@@ -0,0 +1,331 @@
+/*-
+ * Copyright (c) 1999 Jonathan Lemon
+ * 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.
+ *
+ * $Id$
+ */
+
+/*
+ * Disk driver for Compaq SMART RAID adapters.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/buf.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/device.h>
+#include <sys/devicestat.h>
+#include <sys/disklabel.h>
+#include <sys/diskslice.h>
+
+#if NPCI > 0
+#include <machine/bus_memio.h>
+#endif
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <machine/clock.h>
+#include <sys/rman.h>
+
+#include <dev/ida/idareg.h>
+#include <dev/ida/idavar.h>
+
+/* prototypes */
+static void id_drvinit(void);
+static int idprobe(device_t dev);
+static int idattach(device_t dev);
+
+static d_open_t idopen;
+static d_close_t idclose;
+static d_strategy_t idstrategy;
+static d_ioctl_t idioctl;
+static d_psize_t idsize;
+
+#define ID_BDEV_MAJOR 29
+#define ID_CDEV_MAJOR 109
+
+#define WD_BDEV_MAJOR 0
+#define WD_CDEV_MAJOR 3
+
+static struct cdevsw id_cdevsw = {
+ /* open */ idopen,
+ /* close */ idclose,
+ /* read */ physread,
+ /* write */ physwrite,
+ /* ioctl */ idioctl,
+ /* stop */ nostop,
+ /* reset */ noreset,
+ /* devtotty */ nodevtotty,
+ /* poll */ nopoll,
+ /* mmap */ nommap,
+ /* strategy */ idstrategy,
+ /* name */ "id",
+ /* parms */ noparms,
+ /* maj */ ID_CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ idsize,
+ /* flags */ D_DISK,
+ /* maxio */ 0,
+ /* bmaj */ ID_BDEV_MAJOR
+};
+static struct cdevsw stolen_cdevsw;
+
+static devclass_t id_devclass;
+
+static device_method_t id_methods[] = {
+ DEVMETHOD(device_probe, idprobe),
+ DEVMETHOD(device_attach, idattach),
+ { 0, 0 }
+};
+
+static driver_t id_driver = {
+ "id",
+ id_methods,
+ sizeof(struct id_softc)
+};
+
+static __inline struct id_softc *
+idgetsoftc(dev_t dev)
+{
+ int unit;
+
+ unit = dkunit(dev);
+ return ((struct id_softc *)devclass_get_softc(id_devclass, unit));
+}
+
+static int
+idopen(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ struct id_softc *drv;
+ struct disklabel label;
+ int error;
+
+ drv = idgetsoftc(dev);
+ if (drv == NULL)
+ return (ENXIO);
+
+ /* XXX block against race where > 1 person is reading label? */
+
+ bzero(&label, sizeof(label));
+ label.d_type = DTYPE_SCSI; /* XXX should this be DTYPE_RAID? */
+/*
+ strncpy(label.d_typename, ...
+ strncpy(label.d_packname, ...
+*/
+ label.d_secsize = drv->secsize;
+ label.d_nsectors = drv->sectors;
+ label.d_ntracks = drv->heads;
+ label.d_ncylinders = drv->cylinders;
+ label.d_secpercyl = drv->sectors * drv->heads;
+ label.d_secperunit = drv->secperunit;
+
+ /* Initialize slice tables. */
+ error = dsopen("id", dev, fmt, 0, &drv->slices, &label,
+ idstrategy, (ds_setgeom_t *)NULL, &id_cdevsw);
+
+ return (error);
+}
+
+static int
+idclose(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ struct id_softc *drv;
+
+ drv = idgetsoftc(dev);
+ if (drv == NULL)
+ return (ENXIO);
+ dsclose(dev, fmt, drv->slices);
+ return (0);
+}
+
+static int
+idioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
+{
+ struct id_softc *drv;
+ int error;
+
+ drv = idgetsoftc(dev);
+ if (drv == NULL)
+ return (ENXIO);
+
+ error = dsioctl("id", dev, cmd, addr, flag, &drv->slices,
+ idstrategy, (ds_setgeom_t *)NULL);
+
+ if (error == ENOIOCTL)
+ return (ENOTTY);
+
+ return (error);
+}
+
+static int
+idsize(dev_t dev)
+{
+ struct id_softc *drv;
+
+ drv = idgetsoftc(dev);
+ if (drv == NULL)
+ return (ENXIO);
+ return (dssize(dev, &drv->slices, idopen, idclose));
+}
+
+/*
+ * Read/write routine for a buffer. Finds the proper unit, range checks
+ * arguments, and schedules the transfer. Does not wait for the transfer
+ * to complete. Multi-page transfers are supported. All I/O requests must
+ * be a multiple of a sector in length.
+ */
+static void
+idstrategy(struct buf *bp)
+{
+ struct id_softc *drv;
+ int s;
+
+ drv = idgetsoftc(bp->b_dev);
+ if (drv == NULL) {
+ bp->b_error = EINVAL;
+ goto bad;
+ }
+
+ if (dscheck(bp, drv->slices) <= 0)
+ goto done;
+
+ /*
+ * software write protect check
+ */
+ if (drv->flags & DRV_WRITEPROT && (bp->b_flags & B_READ) == 0) {
+ bp->b_error = EROFS;
+ goto bad;
+ }
+
+ /*
+ * If it's a null transfer, return immediately
+ */
+ if (bp->b_bcount == 0)
+ goto done;
+
+ bp->b_driver1 = drv;
+ s = splbio();
+ devstat_start_transaction(&drv->stats);
+ ida_submit_buf(drv->controller, bp);
+ splx(s);
+ return;
+
+bad:
+ bp->b_flags |= B_ERROR;
+
+done:
+ /*
+ * Correctly set the buf to indicate a completed transfer
+ */
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+ return;
+}
+
+void
+id_intr(struct buf *bp)
+{
+ struct id_softc *drv = (struct id_softc *)bp->b_driver1;
+
+ if (bp->b_flags & B_ERROR)
+ bp->b_error = EIO;
+ else
+ bp->b_resid = 0;
+
+ biodone(bp);
+ devstat_end_transaction(&drv->stats,
+ bp->b_bcount - bp->b_resid, DEVSTAT_TAG_NONE,
+ (bp->b_flags & B_READ) ? DEVSTAT_READ : DEVSTAT_WRITE);
+}
+
+static void
+id_drvinit(void)
+{
+ static int devsw_installed = 0;
+
+ if (devsw_installed)
+ return; /* XXX is this needed? */
+
+ cdevsw_add(&id_cdevsw);
+ stolen_cdevsw = id_cdevsw;
+ stolen_cdevsw.d_maj = WD_CDEV_MAJOR;
+ stolen_cdevsw.d_bmaj = WD_BDEV_MAJOR;
+ cdevsw_add(&stolen_cdevsw);
+ devsw_installed = 1;
+}
+
+static int
+idprobe(device_t dev)
+{
+
+ id_drvinit();
+ device_set_desc(dev, "Compaq Logical Drive");
+ return (0);
+}
+
+static int
+idattach(device_t dev)
+{
+ struct ida_drive_info dinfo;
+ struct id_softc *drv;
+ device_t parent;
+ int error;
+
+ drv = (struct id_softc *)device_get_softc(dev);
+ parent = device_get_parent(dev);
+ drv->controller = (struct ida_softc *)device_get_softc(parent);
+ drv->unit = device_get_unit(dev);
+
+ error = ida_command(drv->controller, CMD_GET_LOG_DRV_INFO,
+ &dinfo, sizeof(dinfo), drv->unit, DMA_DATA_IN);
+ if (error) {
+ device_printf(dev, "CMD_GET_LOG_DRV_INFO failed\n");
+ return (ENXIO);
+ }
+
+ drv->cylinders = dinfo.ncylinders;
+ drv->heads = dinfo.nheads;
+ drv->sectors = dinfo.nsectors;
+ drv->secsize = dinfo.secsize;
+ drv->secperunit = dinfo.secperunit;
+
+ /* XXX
+ * other initialization
+ */
+ device_printf(dev, "%uMB (%u sectors), blocksize=%d\n",
+ drv->secperunit / ((1024 * 1024) / drv->secsize),
+ drv->secperunit, drv->secsize);
+
+ devstat_add_entry(&drv->stats, "id", drv->unit, drv->secsize,
+ DEVSTAT_NO_ORDERED_TAGS,
+ DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_OTHER, DEVSTAT_PRIORITY_DA);
+
+ return (0);
+}
+
+DEV_DRIVER_MODULE(id, ida, id_driver, id_devclass,
+ ID_CDEV_MAJOR, ID_BDEV_MAJOR, id_cdevsw, 0, 0);
diff --git a/sys/dev/ida/ida_pci.c b/sys/dev/ida/ida_pci.c
new file mode 100644
index 0000000..95af154
--- /dev/null
+++ b/sys/dev/ida/ida_pci.c
@@ -0,0 +1,199 @@
+/*-
+ * Copyright (c) 1999 Jonathan Lemon
+ * 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.
+ *
+ * $Id$
+ */
+
+#include <pci.h>
+#if NPCI > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/buf.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/devicestat.h>
+
+#include <machine/bus_memio.h>
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+
+#include <dev/ida/idavar.h>
+
+#define IDA_PCI_MAX_DMA_ADDR 0xFFFFFFFF
+#define IDA_PCI_MAX_DMA_COUNT 0xFFFFFFFF
+
+#define IDA_PCI_MEMADDR (PCIR_MAPS + 4) /* Mem I/O Address */
+
+#define IDA_DEVICEID_SMART 0xAE100E11
+
+static struct {
+ u_long board;
+ char *desc;
+} board_id[] = {
+ { 0x4030, "Compaq SMART-2/P array controller" },
+ { 0x4031, "Compaq SMART-2SL array controller" },
+ { 0x4032, "Compaq Smart Array 3200 controller" },
+ { 0x4033, "Compaq Smart Array 3100ES controller" },
+ { 0x4034, "Compaq Smart Array 221 controller" },
+
+ { 0, "" },
+};
+
+static int ida_pci_probe(device_t dev);
+static int ida_pci_attach(device_t dev);
+static void ida_pci_print_child(device_t bus, device_t dev);
+
+static device_method_t ida_pci_methods[] = {
+ DEVMETHOD(device_probe, ida_pci_probe),
+ DEVMETHOD(device_attach, ida_pci_attach),
+
+ DEVMETHOD(bus_print_child, ida_pci_print_child),
+
+ { 0, 0 }
+};
+
+static driver_t ida_pci_driver = {
+ "ida",
+ ida_pci_methods,
+ sizeof(struct ida_softc)
+};
+
+static devclass_t ida_devclass;
+
+static int
+ida_pci_probe(device_t dev)
+{
+ u_long board;
+ int i;
+
+ if (pci_get_devid(dev) == IDA_DEVICEID_SMART) {
+ board = pci_get_subdevice(dev);
+ for (i = 0; board_id[i].board; i++) {
+ if (board_id[i].board == board) {
+ device_set_desc(dev, board_id[i].desc);
+ return (0);
+ }
+ }
+ /*
+ * It's an unknown Compaq SMART device, but assume we
+ * can support it.
+ */
+ device_set_desc(dev, "Unknown Compaq Smart Array controller");
+ return (0);
+ }
+ return (ENXIO);
+}
+
+static int
+ida_pci_attach(device_t dev)
+{
+ struct ida_softc *ida;
+ u_int command;
+ int error, rid;
+
+ command = pci_read_config(dev, PCIR_COMMAND, 1);
+
+ /*
+ * for multiple card types, need to re-determine which type is
+ * being attached here
+ */
+
+ /*
+ * it appears that this board only does MEMIO access.
+ */
+ if ((command & PCIM_CMD_MEMEN) == 0) {
+ device_printf(dev, "Only memory mapped I/O is supported\n");
+ return (ENXIO);
+ }
+
+ ida = (struct ida_softc *)device_get_softc(dev);
+ ida->dev = dev;
+
+ ida->regs_res_type = SYS_RES_MEMORY;
+ ida->regs_res_id = IDA_PCI_MEMADDR;
+ ida->regs = bus_alloc_resource(dev, ida->regs_res_type,
+ &ida->regs_res_id, 0, ~0, 1, RF_ACTIVE);
+ if (ida->regs == NULL) {
+ device_printf(dev, "can't allocate register resources\n");
+ return (ENOMEM);
+ }
+
+ error = bus_dma_tag_create(/*parent*/NULL, /*alignment*/0,
+ /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/MAXBSIZE, /*nsegments*/IDA_NSEG,
+ /*maxsegsize*/BUS_SPACE_MAXSIZE_32BIT, /*flags*/BUS_DMA_ALLOCNOW,
+ &ida->parent_dmat);
+ if (error != 0) {
+ device_printf(dev, "can't allocate DMA tag\n");
+ ida_free(ida);
+ return (ENOMEM);
+ }
+
+ rid = 0;
+ ida->irq_res_type = SYS_RES_IRQ;
+ ida->irq = bus_alloc_resource(dev, ida->irq_res_type, &rid,
+ 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
+ if (ida->irq == NULL) {
+ ida_free(ida);
+ return (ENOMEM);
+ }
+ error = bus_setup_intr(dev, ida->irq, INTR_TYPE_BIO,
+ ida_intr, ida, &ida->ih);
+ if (error) {
+ device_printf(dev, "can't setup interrupt\n");
+ ida_free(ida);
+ return (ENOMEM);
+ }
+
+ error = ida_init(ida);
+ if (error) {
+ ida_free(ida);
+ return (error);
+ }
+ ida_attach(ida);
+ ida->flags = IDA_ATTACHED;
+
+ return (0);
+}
+
+static void
+ida_pci_print_child(device_t bus, device_t dev)
+{
+ printf(" on %s%d", device_get_name(bus), device_get_unit(bus));
+}
+
+DRIVER_MODULE(ida, pci, ida_pci_driver, ida_devclass, 0, 0);
+
+#endif /* NPCI > 0 */
diff --git a/sys/dev/ida/idareg.h b/sys/dev/ida/idareg.h
new file mode 100644
index 0000000..69c3965
--- /dev/null
+++ b/sys/dev/ida/idareg.h
@@ -0,0 +1,99 @@
+/*-
+ * Copyright (c) 1999 Jonathan Lemon
+ * 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.
+ *
+ * $Id$
+ */
+
+/*
+ * #defines and software structures for the Compaq RAID card
+ */
+
+/*
+ * board register offsets
+ */
+#define R_CMD_FIFO 0x04
+#define R_DONE_FIFO 0x08
+#define R_INT_MASK 0x0C
+#define R_STATUS 0x10
+#define R_INT_PENDING 0x14
+
+/*
+ * interrupt mask values
+ */
+#define INT_DISABLE 0x00
+#define INT_ENABLE 0x01
+
+/*
+ * return status codes
+ */
+#define SOFT_ERROR 0x02
+#define HARD_ERROR 0x04
+#define CMD_REJECTED 0x14
+
+/*
+ * command types
+ */
+#define CMD_GET_LOG_DRV_INFO 0x10
+#define CMD_GET_CTRL_INFO 0x11
+#define CMD_SENSE_DRV_STATUS 0x12
+#define CMD_START_RECOVERY 0x13
+#define CMD_GET_PHYS_DRV_INFO 0x15
+#define CMD_BLINK_DRV_LEDS 0x16
+#define CMD_SENSE_DRV_LEDS 0x17
+#define CMD_GET_LOG_DRV_EXT 0x18
+#define CMD_GET_CTRL_INFO 0x11
+#define CMD_READ 0x20
+#define CMD_WRITE 0x30
+#define CMD_WRITE_MEDIA 0x31
+#define CMD_GET_CONFIG 0x50
+#define CMD_SET_CONFIG 0x51
+#define CMD_FLUSH_CACHE 0xc2
+
+/*
+ * command structures
+ */
+struct ida_drive_info {
+ u_int16_t secsize __attribute__ ((packed));
+ u_int32_t secperunit __attribute__ ((packed));
+ u_int16_t ncylinders __attribute__ ((packed));
+ u_int8_t nheads __attribute__ ((packed));
+ u_int8_t signature __attribute__ ((packed));
+ u_int8_t psectors __attribute__ ((packed));
+ u_int16_t wprecomp __attribute__ ((packed));
+ u_int8_t max_acc __attribute__ ((packed));
+ u_int8_t control __attribute__ ((packed));
+ u_int16_t pcylinders __attribute__ ((packed));
+ u_int8_t ptracks __attribute__ ((packed));
+ u_int16_t landing_zone __attribute__ ((packed));
+ u_int8_t nsectors __attribute__ ((packed));
+ u_int8_t checksum __attribute__ ((packed));
+ u_int8_t mirror __attribute__ ((packed));
+};
+
+struct ida_controller_info {
+ u_int8_t num_drvs __attribute__ ((packed));
+ u_int32_t signature __attribute__ ((packed));
+ u_int8_t firm_rev[4] __attribute__ ((packed));
+};
diff --git a/sys/dev/ida/idavar.h b/sys/dev/ida/idavar.h
new file mode 100644
index 0000000..7b8012a
--- /dev/null
+++ b/sys/dev/ida/idavar.h
@@ -0,0 +1,166 @@
+/*-
+ * Copyright (c) 1999 Jonathan Lemon
+ * 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.
+ *
+ * $Id$
+ */
+
+/*
+ * software structures for the Compaq RAID controller
+ */
+
+#ifndef _IDAVAR_H
+#define _IDAVAR_H
+
+struct ida_hdr {
+ u_int8_t drive; /* logical drive */
+ u_int8_t priority; /* block priority */
+ u_int16_t size; /* size of request, in words */
+};
+
+struct ida_req {
+ u_int16_t next; /* offset of next request */
+ u_int8_t command; /* command */
+ u_int8_t error; /* return error code */
+ u_int32_t blkno; /* block number */
+ u_int16_t bcount; /* block count */
+ u_int8_t sgcount; /* number of scatter/gather entries */
+ u_int8_t spare; /* reserved */
+};
+
+struct ida_sgb {
+ u_int32_t length; /* length of S/G segment */
+ u_int32_t addr; /* physical address of block */
+};
+
+#define IDA_NSEG 32 /* maximum number of segments */
+
+/*
+ * right now, this structure totals 276 bytes.
+ */
+struct ida_hardware_qcb {
+ struct ida_hdr hdr; /* 4 */
+ struct ida_req req; /* 12 */
+ struct ida_sgb seg[IDA_NSEG]; /* 256 */
+ struct ida_qcb *qcb; /* 4 - qcb backpointer */
+};
+
+typedef enum {
+ QCB_FREE = 0x0000,
+ QCB_ACTIVE = 0x0001, /* waiting for completion */
+} qcb_state;
+
+#define DMA_DATA_IN 0x0001
+#define DMA_DATA_OUT 0x0002
+#define IDA_COMMAND 0x0004
+#define DMA_DATA_TRANSFER (DMA_DATA_IN | DMA_DATA_OUT)
+
+#define IDA_QCB_MAX 256
+#define IDA_CONTROLLER 0 /* drive "number" for controller */
+
+struct ida_qcb {
+ struct ida_hardware_qcb *hwqcb;
+ qcb_state state;
+ short flags;
+ union {
+ STAILQ_ENTRY(ida_qcb) stqe;
+ SLIST_ENTRY(ida_qcb) sle;
+ } link;
+ bus_dmamap_t dmamap;
+ struct buf *buf; /* buf associated with qcb */
+};
+
+/*
+ * flags for the controller
+ */
+#define IDA_ATTACHED 0x01 /* attached, interrupts okay */
+
+struct ida_softc {
+ device_t dev;
+ int unit;
+
+ int regs_res_type;
+ int regs_res_id;
+ struct resource *regs;
+
+ int irq_res_type;
+ struct resource *irq;
+ void *ih;
+
+ bus_space_tag_t tag;
+ bus_space_handle_t bsh;
+
+ /* various DMA tags */
+ bus_dma_tag_t parent_dmat;
+ bus_dma_tag_t buffer_dmat;
+
+ bus_dma_tag_t hwqcb_dmat;
+ bus_dmamap_t hwqcb_dmamap;
+ bus_addr_t hwqcb_busaddr;
+
+ bus_dma_tag_t sg_dmat;
+
+ int num_drives;
+ int num_qcbs;
+ int flags;
+
+ struct ida_hardware_qcb *hwqcbs; /* HW QCB array */
+ struct ida_qcb *qcbs; /* kernel QCB array */
+ SLIST_HEAD(, ida_qcb) free_qcbs;
+ STAILQ_HEAD(, ida_qcb) qcb_queue;
+ struct buf_queue_head buf_queue;
+};
+
+/*
+ * drive flags
+ */
+#define DRV_WRITEPROT 0x0001
+
+struct id_softc {
+ device_t dev;
+ struct ida_softc *controller;
+ struct diskslices *slices;
+ struct devstat stats;
+ int unit;
+ int cylinders;
+ int heads;
+ int sectors;
+ int secsize;
+ int secperunit;
+ int flags;
+};
+
+extern struct ida_softc *ida_alloc(device_t dev, struct resource *regs,
+ int regs_type, int regs_id, bus_dma_tag_t parent_dmat);
+extern void ida_free(struct ida_softc *ida);
+extern int ida_init(struct ida_softc *ida);
+extern void ida_attach(struct ida_softc *ida);
+extern int ida_command(struct ida_softc *ida, int command, void *data,
+ int datasize, int drive, int flags);
+extern void ida_submit_buf(struct ida_softc *ida, struct buf *bp);
+extern void ida_intr(void *data);
+
+extern void id_intr(struct buf *bp);
+
+#endif /* _IDAVAR_H */
diff --git a/sys/i386/isa/ida.c b/sys/i386/isa/ida.c
deleted file mode 100644
index 919d365..0000000
--- a/sys/i386/isa/ida.c
+++ /dev/null
@@ -1,1786 +0,0 @@
-/*
- * Copyright (c) 1996, 1997, 1998, 1999
- * Mark Dawson and David James. 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 immediately at the beginning of the file, without modification,
- * 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. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * 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.
- *
- * $Id: ida.c,v 1.1 1999/05/25 19:45:17 julian Exp $
- *
- */
-
-/*
- * Compaq SMART disk array controller driver for FreeBSD.
- * Supports the Compaq SMART-2 and SMART-3 families of disk
- * array controllers.
- *
- */
-
-#include "id.h"
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/kernel.h>
-#include <sys/conf.h>
-#include <sys/errno.h>
-#include <sys/malloc.h>
-#include <sys/buf.h>
-#include <sys/file.h>
-#include <sys/stat.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
-#include <i386/isa/isa.h>
-#include <i386/isa/isa_device.h>
-#ifdef DEVFS
-#include <sys/devfsext.h>
-#endif /*DEVFS*/
-/*#include <sys/dkbad.h>*/
-#include <sys/devicestat.h>
-#include <sys/disklabel.h>
-#include <sys/diskslice.h>
-#include <vm/vm.h>
-#include <vm/vm_param.h>
-#include <vm/vm_prot.h>
-#include <vm/pmap.h>
-
-#include <pci.h>
-#include <pci/pcireg.h>
-#include <pci/pcivar.h>
-
-#include <sys/reboot.h>
-extern u_long bootdev;
-
-#define IDA_VERSION 1
-
-#define PAGESIZ 4096
-
-/* IDA wdc vector stealing (cuckoo) control */
-#define IDA_CUCKOO_NEVER 0 /* never steal wdc vectors */
-#define IDA_CUCKOO_ROOTWD 1 /* steal iff rootdev is wd device */
-#define IDA_CUCKOO_ROOTNOTIDA 2 /* steal if rootdev not ida device */
-#define IDA_CUCKOO_ALWAYS 3 /* always steal wdc vectors */
-
-#ifndef IDA_CUCKOO_MODE
-#define IDA_CUCKOO_MODE IDA_CUCKOO_ALWAYS
-#endif
-
-/* IDA PCI controller */
-
-#define PCI_DEVICE_ID_COMPAQ_SMART2P 0xae100e11ul
-#define PCI_CONTROLLER(ctlp) (ctlp->ident == PCI_DEVICE_ID_COMPAQ_SMART2P)
-
-typedef struct ida_pci_reg {
- u_long unknown;
- u_long initiate_fifo;
-#define IDA_PCI_BUSY 1
- u_long complete_fifo;
- u_long interrupt;
-#define IDA_PCI_ENABLE_INTS 1
-#define IDA_PCI_DISABLE_INTS 0
- u_long status;
-#define IDA_PCI_PENDING 1
-#define IDA_PCI_READY 2
-} ida_pci_reg_t;
-
-/* IDA controller register definitions */
-
-#define R_ID0 0xc80 /* id byte 0 */
-#define R_ID1 0xc81 /* id byte 1 */
-#define R_ID2 0xc82 /* id byte 2 */
-#define R_ID3 0xc83 /* id byte 3 */
-#define R_CONF 0xc88 /* global configuration */
-#define R_SYSINT 0xc89 /* system interrupt enable/ctrl */
-#define R_SEM0 0xc8a /* semaphore port 0 */
-#define R_SEM1 0xc8b /* semaphore port 1 */
-#define R_LBELL_E 0xc8c /* local doorbell enable */
-#define R_LBELL_I 0xc8d /* local doorbell int/status */
-#define R_EBELL_E 0xc8e /* EISA doorbell enable */
-#define R_EBELL_I 0xc8f /* EISA doorbell int/status */
-#define R_SUB_ADDR 0xc90 /* submit address */
-#define R_SUB_LEN 0xc94 /* submit cmdlist size */
-#define R_COM_ADDR 0xc98 /* completion address */
-#define R_COM_OFF 0xc9c /* completion request offset */
-#define R_COM_STAT 0xc9e /* completion cmdlist status */
-#define R_INTDEF 0xcc0 /* interrupt definition */
-
-/*
- * BMIC doorbell status codes
- */
-
-#define BMIC_DATA_READY 0x01 /* data ready bit */
-#define BMIC_CHAN_CLEAR 0x02 /* channel clear bit */
-
-
-/* IDA controller command list return status values */
-
-#define IDA_COMPL_OK 0x01 /* command list completed ok */
-#define IDA_NON_FATAL 0x02 /* non-fatal error */
-#define IDA_FATAL 0x04 /* fatal error */
-#define IDA_ABORTED 0x08 /* command list aborted */
-#define IDA_INVAL_REQ 0x10 /* bad request block */
-#define IDA_INVAL_LIST 0x20 /* bad command list */
-#define IDA_AARGH_LIST 0x40 /* totally disastrous command list */
-
-
-/* IDA controller command codes */
-
-#define IDA_GET_DRV_INFO 0x10
-#define IDA_GET_CTL_INFO 0x11
-#define IDA_READ_DATA 0x20
-#define IDA_WRITE_DATA 0x30
-#define IDA_FLUSH_CACHE 0xc2
-
-
-/* Interrupt definition codes */
-
-#define IDA_IRQ_MASK 0xfc
-#define IDA_IRQ_10 0x20
-#define IDA_IRQ_11 0x10
-#define IDA_IRQ_14 0x40
-#define IDA_IRQ_15 0x80
-
-
-/* IDA controller hardware command structure definitions */
-
-typedef u_long physaddr_t;
-
-struct ida_hdr {
- u_long drive:8; /* logical drive */
- u_long priority:8; /* block priority */
- u_long flags:16; /* control flags */
-};
-
-struct ida_req {
- u_long next:16; /* offset of next request */
- u_long command:8; /* command */
- u_long error:8; /* return error code */
- u_long blkno; /* block number */
- u_short bcount; /* block count */
- u_short sgcount; /* number of scatter gather entries */
-
- /* a struct ida_req is followed physically by an array of struct ida_sgb */
-};
-
-struct ida_sgb {
- u_long len; /* length of scatter gather segmmentk */
- physaddr_t addr; /* physical address of block */
-};
-
-
-/* first some handy definitions as FreeBSD gcc doesn't do #pragma pack() */
-
-#define pack_char(name) u_char name[1]
-#define pack_short(name) u_char name[2]
-#define pack_int(name) u_char name[4]
-#define pack_long(name) u_char name[4]
-
-/* ugly, but not inefficient, as it gets evaluated at compile time by gcc */
-
-#define u_unpack(member) ( \
- (sizeof(member) == 1) ? *(u_char *)(member) \
- : (sizeof(member) == 2) ? *(u_short *)(member) \
- : *(u_int *)(member) \
- )
-
-#define s_unpack(member) ( \
- (sizeof(member) == 1) ? *(char *)(member) \
- : (sizeof(member) == 2) ? *(short *)(member) \
- : *(int *)(member) \
- )
-
-/* IDA controller hardware returned data structure definitions */
-
-struct ida_ctl_info {
- pack_char(num_drvs);
- pack_long(signature);
- pack_long(firm_rev);
-};
-
-struct ida_drv_info {
- pack_short(secsize);
- pack_long(secperunit);
- pack_short(ncylinders);
- pack_char(ntracks);
- pack_char(signature);
- pack_char(psectors);
- pack_short(wprecomp);
- pack_char(max_acc);
- pack_char(control);
- pack_short(pcylinders);
- pack_char(ptracks);
- pack_short(landing_zone);
- pack_char(nsectors);
- pack_char(checksum);
- pack_char(mirror);
-};
-
-
-/* IDA driver queue command block */
-
-#define IDA_MAX_SGLEN 32 /* maximum entries in scatter gather list */
-#define IDA_MAX_DRVS_CTLR 8 /* maximum logical drives per controller */
-#define IDA_DEF_PRIORITY 16 /* default priority for command list */
-
-#define IDA_SCSI_TARGET_ID 7
-#define IDA_QCB_MAX 256
-
-struct ida_qcb {
- /* first some hardware specific fields ... */
-
- struct ida_hdr hdr;
- struct ida_req req;
- struct ida_sgb sglist[IDA_MAX_SGLEN];
-
- /* and then some driver queue managment stuff */
-
- u_int flags; /* qcb type */
-#define QCB_FREE 0 /* ready for a new command */
-#define QCB_ACTIVE 1 /* waiting to be sent to the controller */
-#define QCB_SENT 2 /* waiting for interrupt from the controller */
-#define QCB_IMMED 4 /* immediate (non-queued) command */
-#define QCB_IMMED_FAIL 8
- struct ida_qcb *next; /* next ida command block of this type */
- struct ida_qcb *last; /* last ida command block of this type */
- struct buf *buf; /* buf associated with this qcb */
- physaddr_t paddr; /* physical address of this struct */
- struct ida_qcb *nexthash; /* next ida command block with same hash value */
-};
-typedef struct ida_qcb qcb_t;
-
-
-/* IDA driver controller and drive information blocks */
-
-#define QCB_HASH_SIZE 257 /* some suitable prime number */
-#define QCB_HASH(h) ((h) % QCB_HASH_SIZE)
-
-struct ida_drv {
- u_int flags;
-#define ID_INIT 0x0001
-#define ID_WRITEPROT 0x0002
-#define ID_DEV_OPEN 0x0004
- u_int ctl_unit; /* which controller is this drive on */
- u_int drv_unit; /* number of this virtual disk */
- struct ida_drv_info drv_info; /* data from the controller */
- struct diskslices *slices; /* new slice code */
- struct devstat dk_stats; /* devstat entry */
-};
-
-struct ida_ctl {
- u_int ident; /* controller identifier */
- u_int flags;
- u_int iobase;
- u_short inside; /* number of qcbs in the controller */
- u_short max_inside; /* maximum number simulaneously active */
- u_short num_qcbs; /* number of qcbs allocated */
- u_char num_drvs;
- u_char irq;
- u_char com_status; /* status of last completed command list */
- physaddr_t com_addr; /* address of last completed command list */
- u_short com_offset; /* offset of last completed command list */
- qcb_t *freelist; /* linked list of free qcbs */
- qcb_t *send_next; /* doubly-linked list of unsent qcbs */
- qcb_t *send_last; /* because we must treat all jobs equally */
- qcb_t *hashlist[QCB_HASH_SIZE];
-};
-
-extern struct ida_ctl *idadata[NIDA];
-
-
-/* Useful IDA controller IO macro definitions */
-
-#define IDA_DISABLE_INTERRUPT(iobase) outb(iobase + R_SYSINT, 0)
-#define IDA_ENABLE_INTERRUPT(ctlp) outb(ctlp->iobase + R_SYSINT, 1)
-
-#define IDA_SET_READY(ctlp) outb(ctlp->iobase + R_EBELL_E, 1)
-
-#define IDA_DATA_READY(ctlp) ((inb(ctlp->iobase + R_EBELL_I) & 1))
-#define IDA_CHAN_CLEAR(ctlp) ((inb(ctlp->iobase + R_EBELL_I) & 2))
-
-/* enable/disable interrupts on a change of channel clear status (?) */
-
-#define IDA_ENABLE_CHAN(ctlp) \
-outb(ctlp->iobase + R_EBELL_E, inb(ctlp->iobase + R_EBELL_E) | 2)
-#define IDA_DISABLE_CHAN(ctlp) \
-outb(ctlp->iobase + R_EBELL_E, inb(ctlp->iobase + R_EBELL_E) & ~0x2)
-
-/* acknowledge the completion of a command */
-
-#define IDA_ACK_CMD_COM(ctlp) \
-(outb(ctlp->iobase + R_EBELL_I, 1), outb(ctlp->iobase + R_LBELL_I, 2))
-
-/* set submission details for a command list */
-
-#define IDA_SET_SUB_ADDR(ctlp, addr) outl(ctlp->iobase + R_SUB_ADDR, addr)
-#define IDA_SET_SUB_LEN(ctlp, size) outw(ctlp->iobase + R_SUB_LEN, size)
-
-/* get completion details for a command list */
-
-#define IDA_GET_COM_ADDR(ctlp) inl(ctlp->iobase + R_COM_ADDR)
-#define IDA_GET_COM_OFFSET(ctlp) inw(ctlp->iobase + R_COM_OFF)
-#define IDA_GET_COM_STATUS(ctlp) inb(ctlp->iobase + R_COM_STAT)
-
-
-#define IDA_READ_EBELL_I(ctlp) inb(ctlp->iobase + R_EBELL_I)
-#define IDA_READ_EBELL_E(ctlp) inb(ctlp->iobase + R_EBELL_E)
-#define IDA_READ_LBELL_I(ctlp) inb(ctlp->iobase + R_LBELL_I)
-#define IDA_SET_EBELL_I(ctlp, n) outb(ctlp->iobase + R_EBELL_I, n)
-#define IDA_SET_LBELL_I(ctlp, n) outb(ctlp->iobase + R_LBELL_I, n)
-
-#define JOB_SUCCESS 0
-#define JOB_FAILURE 1
-#define JOB_ABORTED 2
-
-/* debugging aids */
-
-#ifdef IDADEBUG
-# define IDA_MAXQCBS (1<<0)
-# define IDA_SHOWQCBS (1<<1)
-# define IDA_SHOWSUBS (1<<2)
-# define IDA_SHOWINTS (1<<3)
-# define IDA_SHOWCMDS (1<<4)
-# define IDA_SHOWMISC (1<<5)
-void ida_print_qcb(qcb_t *qcbp);
-void ida_print_active_qcb(int unit);
-static int ida_debug = 0;
-SYSCTL_INT(_debug, OID_AUTO, ida_debug, CTLFLAG_RW, &ida_debug, 0, "");
-#endif
-
-static int ida_soft_errors = 0;
-SYSCTL_INT(_debug, OID_AUTO, ida_soft_errors,
- CTLFLAG_RW, &ida_soft_errors, 0, "");
-
-/* EISA probe and board identification definitions */
-
-#define MAX_EISA_SLOT 16
-#define IDA_EISA_PROD_ID 0x40
-
-union eisa_id {
- u_int value;
- struct {
- u_int rev:8;
- u_int prod:8;
- u_int mfr2:5;
- u_int mfr1:5;
- u_int mfr0:5;
- } split;
-};
-
-
-/* test the manufacturer ID within an EISA board ID */
-
-#define EISA_MFR_EQ(ident, mfr) ( \
- (ident).split.mfr0 + '@' == (mfr)[0] && \
- (ident).split.mfr1 + '@' == (mfr)[1] && \
- (ident).split.mfr2 + '@' == (mfr)[2] \
- )
-
-/* generates a list of EISA board ID values, suitable for a printf */
-
-#define EISA_ID_LIST(ident) \
-(ident).split.mfr0 + '@', \
- (ident).split.mfr1 + '@', \
- (ident).split.mfr2 + '@', \
- (ident).split.prod, \
- (ident).split.rev
-
-extern void DELAY(int millisecs);
-
-/* FreeBSD IDA driver forward function definitions */
-
-static d_open_t idopen;
-static d_read_t idread;
-static d_write_t idwrite;
-static d_close_t idclose;
-static d_strategy_t idstrategy;
-static d_ioctl_t idioctl;
-static d_dump_t iddump;
-static d_psize_t idsize;
-
-static pci_inthand_t idaintr;
-
-static const char *ida_pci_probe(pcici_t tag, pcidi_t type);
-static void ida_pci_attach(pcici_t config_id, int unit);
-
-static int ida_eisa_probe __P((struct isa_device *dev));
-static int ida_eisa_attach __P((struct isa_device *dev));
-static int ida_poll __P((int unit, int wait));
-
-static void idaminphys __P((struct buf *bp));
-static void ida_start __P((int unit));
-
-static int ida_get_ctl_info(int unit);
-static int ida_attach_drives(int unit);
-void ida_done(int cntlr, qcb_t *qcbp, int state);
-static void ida_queue_buf(int cntlr, struct buf *bp);
-static void ida_newqueue(int cntlr);
-static qcb_t *ida_dequeue(int cntlr);
-static void ida_enqueue(int cntlr, qcb_t *qcbp);
-static int ida_submit(int unit, qcb_t *qcbp, int size);
-static int ida_submit_wait(int unit, qcb_t *qcbp, int size);
-static qcb_t *ida_get_qcb(int unit);
-static void ida_free_qcb(int unit, qcb_t *qcbp);
-static qcb_t *ida_qcb_phys_kv(struct ida_ctl *ida, physaddr_t ida_qcb_phys);
-
-static struct cdevsw id_cdevsw;
-
-struct isa_driver idadriver = {
- ida_eisa_probe,
- ida_eisa_attach,
- "ida"
-};
-
-static u_long ida_pci_count;
-
-static struct pci_device ida_pci_driver = {
- "ida",
- ida_pci_probe,
- ida_pci_attach,
- &ida_pci_count
-};
-
-DATA_SET (pcidevice_set, ida_pci_driver);
-
-/* definitions for stealing wd driver's vectors */
-
-#define ID_BDMAJ 29
-#define ID_CDMAJ 109
-#define WD_BDMAJ 0
-#define WD_CDMAJ 3
-
-struct isa_driver wdcdriver;
-static int stub_probe __P((struct isa_device *dev));
-static int stub_attach __P((struct isa_device *dev));
-
-static struct isa_driver nodriver = {
- stub_probe,
- stub_attach,
- "stub"
-};
-
-/* steal the wdc driver's vectors if we have booted off a wd device */
-
-static
-void
-ida_cuckoo_wdc(void)
-{
- int cuckoo = IDA_CUCKOO_MODE;
- int steal = 0;
- char *mode;
- int major = B_TYPE(bootdev);
-
- if (cuckoo == IDA_CUCKOO_NEVER) {
- mode = "never";
- } else if (cuckoo == IDA_CUCKOO_ROOTWD) {
- mode = "rootwd";
- steal = (major == WD_BDMAJ);
- } else if (cuckoo == IDA_CUCKOO_ROOTNOTIDA) {
- mode = "notida";
- /* check for magic value of 3 rather than ID_BDMAJ as boot code
- * pretends we are a wt device (not normally bootable)
- */
- steal = (major != 3);
- } else {
- mode = "always";
- steal = 1;
- }
-
- printf("ida: wdc vector stealing %s (mode = %s, boot major = %d)\n",
- (steal ? "on" : "off"), mode, major);
- if (!steal) return;
-
- /* OK - we have a controller, so steal wd driver's vectors */
- wdcdriver = nodriver;
- bdevsw[WD_BDMAJ]->d_open = cdevsw[WD_CDMAJ]->d_open = idopen;
- bdevsw[WD_BDMAJ]->d_close = cdevsw[WD_CDMAJ]->d_close = idclose;
- bdevsw[WD_BDMAJ]->d_read = cdevsw[WD_CDMAJ]->d_read = idread;
- bdevsw[WD_BDMAJ]->d_write = cdevsw[WD_CDMAJ]->d_write = idwrite;
- bdevsw[WD_BDMAJ]->d_strategy = cdevsw[WD_CDMAJ]->d_strategy = idstrategy;
- bdevsw[WD_BDMAJ]->d_ioctl = cdevsw[WD_CDMAJ]->d_ioctl = idioctl;
- bdevsw[WD_BDMAJ]->d_dump = iddump;
- bdevsw[WD_BDMAJ]->d_psize = idsize;
- return;
-}
-
-static struct ida_ctl *idadata[NIDA]; /* controller structures */
-static struct ida_drv *id_drive[NID]; /* table of drives */
-static int id_unit = 0; /* number of drives found */
-
-/* general purpose data buffer for 'special' IDA driver commands */
-
-union ida_buf {
- char pad[512];
- struct ida_ctl_info ctl;
- struct ida_drv_info drv;
-} ida_buf;
-
-static int
-stub_probe(struct isa_device *dev)
-{
- return 0;
-}
-
-static int
-stub_attach(struct isa_device *dev)
-{
- return 0;
-}
-
-const char *
-ida_pci_probe(pcici_t tag, pcidi_t type)
-{
- switch (type) {
- case PCI_DEVICE_ID_COMPAQ_SMART2P:
- return "Compaq SMART-2/P array controller";
- break;
- default:
- break;
- }
- return NULL;
-}
-
-static void
-ida_pci_attach(pcici_t config_id, int unit)
-{
- ida_pci_reg_t *reg;
- struct ida_ctl *ctlp;
- u_long id;
- vm_offset_t paddr, vaddr;
-
- id = pci_conf_read(config_id, PCI_ID_REG);
- switch (id) {
- case PCI_DEVICE_ID_COMPAQ_SMART2P:
- break;
- default:
- break;
- }
-
- if (!pci_map_mem(config_id, 0x14, &vaddr, &paddr)) {
- printf("ida: map failed.\n");
- return;
- }
-
- /* allocate and initialise a storage area for this controller */
- if (idadata[unit]) {
- printf("ida%d: controller structure already allocated\n", unit);
- return;
- }
- if ((ctlp = malloc(sizeof(struct ida_ctl), M_TEMP, M_NOWAIT)) == NULL) {
- printf("ida%d: unable to allocate controller structure\n", unit);
- return;
- }
-
- idadata[unit] = ctlp;
- bzero(ctlp, sizeof(struct ida_ctl));
- ctlp->ident = id;
- ctlp->iobase = vaddr;
-
- /* Install the interrupt handler. */
- if (!pci_map_int (config_id, idaintr, (void *)unit, &bio_imask)) {
- printf ("ida%d: failed to assign an interrupt handler\n", unit);
- free((caddr_t)ctlp, M_DEVBUF);
- idadata[unit] = 0;
- return;
- }
-
- if (!(ida_get_ctl_info(unit) && ida_attach_drives(unit))) {
- return;
- }
-
- reg = (ida_pci_reg_t *) vaddr;
- reg->interrupt = IDA_PCI_ENABLE_INTS;
- ida_cuckoo_wdc();
- return;
-}
-
-int
-ida_eisa_probe(struct isa_device *dev)
-{
- static u_int ida_used = 0;
- u_int slot;
- u_int port;
- u_char intdef;
- u_char irq;
- int unit = dev->id_unit;
- union eisa_id ident;
- struct ida_ctl *ctlp;
-
- if (dev->id_iobase) {
- /* check out the configured iobase if given one */
- slot = dev->id_iobase / 0x1000;
- if (slot == 0 || slot > MAX_EISA_SLOT) {
- printf("ida: port address (0x%x) out of range\n", dev->id_iobase);
- return 0;
- }
- } else {
- /* otherwise, search from the beginning for an unused slot to check out */
- slot = 1;
- }
-
- while (1) {
- while (ida_used & (1 << slot)) {
- if (slot++ == MAX_EISA_SLOT) return 0;
- }
-
- ida_used |= (1 << slot);
- port = slot * 0x1000;
-
- /* read the EISA identification bytes */
- ident.value = inb(port + R_ID0);
- ident.value <<= 8;
- ident.value |= inb(port + R_ID1);
- ident.value <<= 8;
- ident.value |= inb(port + R_ID2);
- ident.value <<= 8;
- ident.value |= inb(port + R_ID3);
-
- /* check that the card is the right type ? */
- if (EISA_MFR_EQ(ident, "CPQ") && ident.split.prod == IDA_EISA_PROD_ID) {
- break;
- }
-
- /* if we were config'ed with an iobase, then don't probe any more slots */
- if (dev->id_iobase) return 0;
- }
-
- /* disable interrupts and find out what interrupt this controller uses */
- IDA_DISABLE_INTERRUPT(port);
-
- intdef = inb(port + R_INTDEF);
- switch (intdef & IDA_IRQ_MASK) {
- case IDA_IRQ_10:
- irq = 10;
- break;
-
- case IDA_IRQ_11:
- irq = 11;
- break;
-
- case IDA_IRQ_14:
- irq = 14;
- break;
-
- case IDA_IRQ_15:
- irq = 15;
- break;
-
- default:
- printf("ida: slot %d bogus interrupt setting (0x%02x)\n", slot, intdef);
- return 0;
- }
- dev->id_irq = (1 << irq);
- dev->id_drq = -1;
-
- /* allocate and initialise a storage area for this controller */
- if (idadata[unit]) {
- printf("ida%d: controller structure already allocated\n", unit);
- return 0;
- }
- if ((ctlp = malloc(sizeof(struct ida_ctl), M_TEMP, M_NOWAIT)) == NULL) {
- printf("ida%d: unable to allocate controller structure\n", unit);
- return 0;
- }
- idadata[unit] = ctlp;
-
- bzero(ctlp, sizeof(struct ida_ctl));
- ctlp->iobase = dev->id_iobase = port;
- ctlp->ident = ident.value;
- ctlp->irq = irq;
-
- if (ida_get_ctl_info(unit) == 0) {
- return 0;
- }
-
- /* return range of io ports used */
- return 0x1000;
-}
-
-int
-ida_get_ctl_info(int unit)
-{
- struct ida_ctl *ctlp = idadata[unit];
- qcb_t qcb;
- qcb_t *qcbp = &qcb;
-
- ida_newqueue(unit);
- /* controller capacity statistics */
- ctlp->inside = 0;
- ctlp->max_inside = 0;
-
- /* ask the controller to tell us about itself with an IDA_GET_CTL_INFO */
- bzero(qcbp, sizeof(qcb_t));
- qcbp->paddr = vtophys(qcbp);
- if (PCI_CONTROLLER(ctlp)) {
- qcbp->hdr.priority = 0x00;
- qcbp->hdr.flags = 0x24;
- } else {
- qcbp->hdr.priority = IDA_DEF_PRIORITY;
- qcbp->hdr.flags = 0x12;
- }
- qcbp->req.command = IDA_GET_CTL_INFO;
- qcbp->req.bcount = 1;
- qcbp->req.sgcount = 1;
- qcbp->sglist[0].len = sizeof(ida_buf);
- qcbp->sglist[0].addr = vtophys(&ida_buf);
-
- if (ida_submit_wait(unit, qcbp, sizeof(struct ida_qcb))) {
- printf("ida%d: idasubmit failed on IDA_GET_CTL_INFO\n", unit);
- return 0;
- }
-
- if (!PCI_CONTROLLER(ctlp)) {
- if (ctlp->com_status != IDA_COMPL_OK) {
- printf("ida%d: bad status 0x%02x from IDA_GET_CTL_INFO\n",
- unit, ctlp->com_status);
- return 0;
- }
- }
-
- /* got the information at last, print it and note the number of drives */
- printf("ida%d: drvs=%d firm_rev=%c%c%c%c\n", unit,
- u_unpack(ida_buf.ctl.num_drvs), ida_buf.ctl.firm_rev[0],
- ida_buf.ctl.firm_rev[1], ida_buf.ctl.firm_rev[2],
- ida_buf.ctl.firm_rev[3]);
- ctlp->num_drvs = u_unpack(ida_buf.ctl.num_drvs);
-
- return 1;
-}
-
-int
-ida_attach_drives(int cntlr)
-{
- struct ida_ctl *ctlp = idadata[cntlr];
- qcb_t qcb;
- qcb_t *qcbp = &qcb;
- struct ida_drv *drv;
- int drive;
- int unit;
-
- /* prepare to interrogate the drives */
- bzero(qcbp, sizeof(qcb_t));
- qcbp->req.command = IDA_GET_DRV_INFO;
- qcbp->paddr = vtophys(qcbp);
- if (PCI_CONTROLLER(ctlp)) {
- qcbp->hdr.priority = 0x00;
- qcbp->hdr.flags = 0x24;
- } else {
- qcbp->hdr.priority = IDA_DEF_PRIORITY;
- qcbp->hdr.flags = 0x12;
- }
- qcbp->req.bcount = 1;
- qcbp->req.sgcount = 1;
- qcbp->sglist[0].len = sizeof(ida_buf);
- qcbp->sglist[0].addr = vtophys(&ida_buf);
-
- for (drive = 0 ; drive < ctlp->num_drvs ; drive++) {
- qcbp->hdr.drive = drive;
-
- if (ida_submit_wait(cntlr, qcbp, sizeof(struct ida_qcb))) {
- printf("ida%d: ida_submit_wait failed on IDA_GET_DRV_INFO\n", cntlr);
- return 0;
- }
-
- if (!PCI_CONTROLLER(ctlp)) {
- if (ctlp->com_status != IDA_COMPL_OK) {
- printf("ida%d: bad status 0x%02x from IDA_GET_DRV_INFO\n",
- cntlr, ctlp->com_status);
- return 0;
- }
- }
-
- if ((drv = malloc(sizeof(struct ida_drv), M_TEMP, M_NOWAIT)) == NULL) {
- printf("ida%d: unable to allocate drive structure\n", cntlr);
- return 0;
- }
-
- bzero(drv, sizeof(struct ida_drv));
- drv->ctl_unit = cntlr;
- drv->drv_unit = drive;
- drv->drv_info = ida_buf.drv;
- drv->flags |= ID_INIT;
-
- unit = id_unit;
- id_unit++; /* XXX unsure if this is the right way to do things */
- id_drive[unit] = drv;
-
- printf("ida%d: unit %d (id%d): <%s>\n",
- cntlr, drive, unit, "Compaq Logical Drive");
- printf("id%d: %luMB (%lu total sec), ",
- unit,
- (u_long)(u_unpack(drv->drv_info.secperunit) / 2048)
- * (u_unpack(drv->drv_info.secsize) / 512),
- (u_long)u_unpack(drv->drv_info.secperunit));
- printf("%lu cyl, %lu head, %lu sec, bytes/sec %lu\n",
- (u_long)u_unpack(drv->drv_info.ncylinders),
- (u_long)u_unpack(drv->drv_info.ntracks),
- (u_long)u_unpack(drv->drv_info.nsectors),
- (u_long)u_unpack(drv->drv_info.secsize));
-
- /*
- * Export the drive to the devstat interface.
- */
- devstat_add_entry(&drv->dk_stats, "id",
- unit, (u_int32_t)drv->drv_info.secsize,
- DEVSTAT_NO_ORDERED_TAGS,
- DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_OTHER);
-
-#ifdef IDADEBUG
- if (ida_debug & IDA_SHOWMISC) {
- printf("ida%d: drive %d secsize=%d secperunit=%d ncylinders=%d ntracks=%d\n",
- unit,
- drive,
- u_unpack(ida_buf.drv.secsize),
- u_unpack(ida_buf.drv.secperunit),
- u_unpack(ida_buf.drv.ncylinders),
- u_unpack(ida_buf.drv.ntracks));
- printf(" signature=0x%02x psectors=%d wprecomp=%d max_acc=%d control=0x%02x\n",
- u_unpack(ida_buf.drv.signature),
- u_unpack(ida_buf.drv.psectors),
- u_unpack(ida_buf.drv.wprecomp),
- u_unpack(ida_buf.drv.max_acc),
- u_unpack(ida_buf.drv.control));
- printf(" pcylinders=%d ptracks=%d landing_zone=%d nsectors=%d checksum=0x%02x\n",
- u_unpack(ida_buf.drv.pcylinders),
- u_unpack(ida_buf.drv.ptracks),
- u_unpack(ida_buf.drv.landing_zone),
- u_unpack(ida_buf.drv.nsectors),
- u_unpack(ida_buf.drv.checksum));
- }
-#endif
- }
-
- return 1;
-}
-
-/* Attach all the sub-devices we can find. */
-int
-ida_eisa_attach(struct isa_device *dev)
-{
- int cntlr = dev->id_unit;
- struct ida_ctl *ctlp = idadata[cntlr];
-
- if (ida_attach_drives(cntlr)) {
- IDA_ENABLE_INTERRUPT(ctlp);
- IDA_SET_READY(ctlp);
- ida_cuckoo_wdc();
- return 1;
- } else {
- return 0;
- }
-}
-
-/*
- * Initialize a drive.
- */
-int
-idopen(dev_t dev, int flags, int fmt, struct proc *p)
-{
- struct ida_drv *drv;
- int part = dkpart(dev);
- int unit = dkunit(dev);
- struct disklabel label;
- int err;
-
- if (unit >= NID || part >= MAXPARTITIONS) /* bounds check */
- return(ENXIO);
-
- drv = id_drive[unit];
- if (!drv || !(drv->flags & ID_INIT)) /* drive not initialised */
- return(ENXIO);
-
- drv->flags |= ID_DEV_OPEN;
-
- /* knock up a label for the whole disk. */
- bzero(&label, sizeof label);
- label.d_secsize = u_unpack(drv->drv_info.secsize);
- label.d_nsectors = u_unpack(drv->drv_info.nsectors);
- label.d_ntracks = u_unpack(drv->drv_info.ntracks);
- label.d_ncylinders = u_unpack(drv->drv_info.ncylinders);
- label.d_secpercyl =
- u_unpack(drv->drv_info.ntracks) * u_unpack(drv->drv_info.nsectors);
- if (label.d_secpercyl == 0)
- label.d_secpercyl = 100; /* prevent accidental division by zero */
- label.d_secperunit = u_unpack(drv->drv_info.secperunit);
-
- /* Initialize slice tables. */
- if ((err = dsopen("id", dev, fmt, 0, &drv->slices, &label, idstrategy,
- (ds_setgeom_t *)NULL, &id_cdevsw)) == NULL) {
- return 0;
- }
-
- if (!dsisopen(drv->slices)) {
- drv->flags &= ~ID_DEV_OPEN;
- }
- return err;
-}
-
-/* ARGSUSED */
-int
-idclose(dev_t dev, int flags, int fmt, struct proc *p)
-{
- struct ida_drv *drv;
- int part = dkpart(dev);
- int unit = dkunit(dev);
-
- if (unit >= NID || part >= MAXPARTITIONS) /* bounds check */
- return(ENXIO);
-
- drv = id_drive[unit];
- dsclose(dev, fmt, drv->slices);
- if (!dsisopen(drv->slices)) {
- drv->flags &= ~ID_DEV_OPEN;
- }
- return 0;
-}
-
-
-
-int
-idioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
-{
- struct ida_drv *drv;
- int part = dkpart(dev);
- int unit = dkunit(dev);
- int err;
-
- if (unit >= NID || part >= MAXPARTITIONS ||
- !(drv = id_drive[unit]) || !(drv->flags & ID_INIT)) /* sanity check */
- return(ENXIO);
-
- err = dsioctl("id", dev, cmd, addr, flag, &drv->slices,
- idstrategy, (ds_setgeom_t *)NULL);
-
- if (err != -1)
- return (err);
-
- if (dkpart(dev) != RAW_PART)
- return (ENOTTY);
-
- return (0);
-}
-static int
-idread(dev_t dev, struct uio *uio, int ioflag)
-{
- return (physio(idstrategy, NULL, dev, 1, minphys, uio));
-}
-
-static int
-idwrite(dev_t dev, struct uio *uio, int ioflag)
-{
- return (physio(idstrategy, NULL, dev, 0, minphys, uio));
-}
-
-
-/* Read/write routine for a buffer. Finds the proper unit, range checks
- * arguments, and schedules the transfer. Does not wait for the transfer
- * to complete. Multi-page transfers are supported. All I/O requests must
- * be a multiple of a sector in length.
- */
-void
-idstrategy(struct buf *bp)
-{
- int unit = dkunit(bp->b_dev);
- struct ida_drv *drv;
- int opri;
-
- if (unit >= NID) {
- printf("ida: unit out of range\n");
- bp->b_error = EINVAL;
- goto bad;
- }
-
- if (!(drv = id_drive[unit]) || !(drv->flags & ID_INIT)) {
- printf("id%d: drive not initialised\n", unit);
- bp->b_error = EINVAL;
- goto bad;
- }
-
- if (bp->b_blkno < 0) {
- printf("id%d: negative block requested\n", unit);
- bp->b_error = EINVAL;
- goto bad;
- }
-
- if (bp->b_bcount % DEV_BSIZE != 0) { /* bounds check */
- printf("id%d: count (%lu) not a multiple of a block\n",
- unit, bp->b_bcount);
- bp->b_error = EINVAL;
- goto bad;
- }
-
- idaminphys(bp); /* adjust the transfer size */
-
- /* "soft" write protect check */
- if ((drv->flags & ID_WRITEPROT) && (bp->b_flags & B_READ) == 0) {
- bp->b_error = EROFS;
- goto bad;
- }
-
- /* If it's a null transfer, return immediately */
- if (bp->b_bcount == 0) {
- goto done;
- }
-
- if (dscheck(bp, drv->slices) <= 0) {
- goto done;
- }
-
- opri = splbio();
- ida_queue_buf(unit, bp);
- devstat_start_transaction(&drv->dk_stats);
- ida_start(drv->ctl_unit); /* hit the appropriate controller */
- splx(opri);
-
- return /*0*/;
-
-bad:
- bp->b_flags |= B_ERROR;
-
-done:
- /* correctly set the buf to indicate a completed xfer */
- bp->b_resid = bp->b_bcount;
- biodone(bp);
- return /*0*/;
-}
-
-
-void
-idaminphys(bp)
-struct buf *bp;
-{
- /* assumes each page requires an sgb entry */
- int max = (IDA_MAX_SGLEN - 1) * PAGESIZ;
- if (bp->b_bcount > max) {
- bp->b_bcount = max;
- }
-}
-
-
-/* Get a free qcb.
- * If there are none, see if we can allocate a new one.
- * If so, put it in the hash table too,
- * otherwise either return an error or sleep.
- */
-static qcb_t *
-ida_get_qcb(int unit)
-{
- struct ida_ctl *ida = idadata[unit];
- unsigned opri = 0;
- qcb_t *qcbp;
- int hashnum;
-
- opri = splbio();
-
- /* if the freelist is empty - create a qcb until limit is reached */
- while (!(qcbp = ida->freelist)) {
- if (ida->num_qcbs < IDA_QCB_MAX) {
- qcbp = (qcb_t *)malloc(sizeof(qcb_t), M_TEMP, M_NOWAIT);
- if (qcbp) {
- bzero(qcbp, sizeof(qcb_t));
- ida->num_qcbs++;
- qcbp->flags = QCB_ACTIVE;
-#ifdef IDADEBUG
- if (ida_debug & IDA_SHOWQCBS)
- printf("ida_get_qcb%d: qcb %d created\n",
- unit, ida->num_qcbs);
-#endif
- /* Put in the phystokv hash table. */
- /* Never gets taken out. */
- qcbp->paddr = vtophys(qcbp);
- hashnum = QCB_HASH(qcbp->paddr);
- qcbp->nexthash = ida->hashlist[hashnum];
- ida->hashlist[hashnum] = qcbp;
- } else {
- printf("ida%d: Can't malloc QCB\n", unit);
- } goto gottit;
- } else {
- /* reached maximum allocation of qcbs - sleep until one is freed */
- tsleep((caddr_t)&ida->freelist, PRIBIO, "idaqcb", 0);
- }
- } if (qcbp) {
- /* qet the qcb from from the (non-empty) free list */
- ida->freelist = qcbp->next;
- qcbp->flags = QCB_ACTIVE;
- }
-gottit:
- splx(opri);
-
-#ifdef IDADEBUG
- if (ida_debug & IDA_SHOWQCBS)
- printf("ida_get_qcb%d: returns 0x%x\n", unit, qcbp);
-#endif
-
- return (qcbp);
-}
-
-
-/* Return a qcb to the free list */
-static void
-ida_free_qcb(int unit, qcb_t *qcbp)
-{
- unsigned int opri = 0;
- struct ida_ctl *ida = idadata[unit];
-
-#ifdef IDADEBUG
- if (ida_debug & IDA_SHOWQCBS)
- printf("ida_free_qcb%d: freeing 0x%x\n", unit, qcbp);
-#endif
-
- opri = splbio();
-
- qcbp->next = ida->freelist;
- ida->freelist = qcbp;
- qcbp->flags = QCB_FREE;
-
- /* if the free list was empty, wakeup anyone sleeping */
-
- if (!qcbp->next) {
-#ifdef IDADEBUG
- if (ida_debug & IDA_SHOWQCBS)
- printf("ida_free_qcb%d: about to wakeup 0x%x queue\n",
- unit, &ida->freelist);
-#endif
- wakeup((caddr_t)&ida->freelist);
- }
-
- splx(opri);
-}
-
-/* Find the ida_qcb having a given physical address */
-qcb_t *
-ida_qcb_phys_kv(ida, ida_qcb_phys)
-struct ida_ctl *ida;
-physaddr_t ida_qcb_phys;
-{
- int hash = QCB_HASH(ida_qcb_phys);
- qcb_t *qcbp = ida->hashlist[hash];
-
- while (qcbp) {
- if (qcbp->paddr == ida_qcb_phys)
- break;
- qcbp = qcbp->nexthash;
- }
-
-#ifdef IDADEBUG
- if (ida_debug & IDA_SHOWQCBS)
- printf("ida?: ida_qcb_phys_kv(0x%x) = 0x%x\n", ida_qcb_phys, qcbp);
-#endif
-
- return qcbp;
-}
-
-void
-ida_queue_buf(int unit, struct buf *bp)
-{
- struct ida_drv *drv = id_drive[unit];
- int cntlr = drv->ctl_unit;
- qcb_t *qcbp = ida_get_qcb(cntlr); /* may cause us to wait */
- struct ida_ctl *ida = idadata[cntlr];
- unsigned int datalen = bp->b_bcount;
- int thiskv = (int)bp->b_data;
- physaddr_t thisphys = vtophys(thiskv);
- int nsgb = 0; /* number of scatter/gather blocks used */
- struct ida_sgb *sg = &(qcbp->sglist[0]);
-
- /* fill in the qcb command header */
-
- if (PCI_CONTROLLER(ida)) {
- qcbp->hdr.priority = 0x00;
- qcbp->hdr.flags = 0x24;
- } else {
- qcbp->hdr.priority = IDA_DEF_PRIORITY;
- qcbp->hdr.flags = 0x10;
- }
- qcbp->hdr.drive = drv->drv_unit; /* logical drive number */
- qcbp->buf = bp; /* the buf this command came from */
-
- /* set up the scatter-gather list in the qcb */
-
- while ((datalen) && (nsgb < IDA_MAX_SGLEN)) {
- int bytes_this_seg = 0;
- physaddr_t nextphys;
-
- /* put in the base address */
- sg->addr = thisphys;
-
- /* do it at least once */
- nextphys = thisphys;
- while ((datalen) && (thisphys == nextphys)) {
- int bytes_this_page;
-
- /* This page is contiguous (physically) with the the last, */
- /* just extend the length */
-
- /* how far to the end of the page ... */
- nextphys = (thisphys & (~(PAGESIZ - 1))) + PAGESIZ;
- bytes_this_page = nextphys - thisphys;
- /* ... or to the end of the data */
- bytes_this_page = min(bytes_this_page, datalen);
- bytes_this_seg += bytes_this_page;
- datalen -= bytes_this_page;
-
- /* get ready for the next page */
- thiskv = (thiskv & (~(PAGESIZ - 1))) + PAGESIZ;
- if (datalen)
- thisphys = vtophys(thiskv);
- }
-
- /* next page isn't contiguous, finish the seg */
- sg->len = bytes_this_seg;
- sg++;
- nsgb++;
- }
-
- if (datalen) { /* still data => command block too small */
- printf("ida_queue_buf%d: more than %d scatter/gather blocks needed\n",
- cntlr, IDA_MAX_SGLEN);
- bp->b_error = EIO;
- bp->b_flags |= B_ERROR;
- biodone(bp);
- return;
- }
-
- /* fill-in the I/O request block */
-
- qcbp->req.error = 0;
- qcbp->req.next = 0;
- qcbp->req.blkno = bp->b_pblkno;
- qcbp->req.bcount = bp->b_bcount >> 9;
- qcbp->req.sgcount = nsgb;
- qcbp->req.command = (bp->b_flags & B_READ ? IDA_READ_DATA : IDA_WRITE_DATA);
-
-#ifdef IDADEBUG
- if (ida_debug & IDA_SHOWQCBS) {
- printf("ida_rw%d: queuing:\n", cntlr);
- ida_print_qcb(qcbp);
- }
-#endif
-
- /* queue for submission to the controller */
- ida_enqueue(cntlr, qcbp);
-}
-
-
-void
-ida_start(int cntlr)
-{
- struct ida_ctl *ida = idadata[cntlr];
- qcb_t *qcbp;
- int count = 0;
- int opri = splbio();
-
- if (!ida->send_next) { /* check there is a job in the queue */
- splx(opri);
- return;
- }
-
- if (PCI_CONTROLLER(ida)) {
- ida_pci_reg_t *reg = (ida_pci_reg_t *)ida->iobase;
- u_int fifo = reg->initiate_fifo;
- if (fifo == 1) {
- splx(opri);
- return; /* not sent - must try again later */
- }
-
- /* submit upto 16 jobs at once into the initiate fifo */
- while (count < 16 && (fifo = reg->initiate_fifo) != 1
- && (qcbp = ida_dequeue(cntlr))) {
- reg->initiate_fifo = qcbp->paddr;
- qcbp->flags = QCB_SENT;
- ida->inside++;
- count++;
- }
- } else {
-
- if (!IDA_CHAN_CLEAR(ida)) {
- IDA_ENABLE_CHAN(ida);
- splx(opri);
- return;
- }
-
- qcbp = ida_dequeue(cntlr);
-
-#ifdef IDADEBUG
- if (ida_debug & IDA_SHOWQCBS)
- printf("ida%d: ida_start: sending 0x%x\n", cntlr, qcbp);
-#endif
-
- IDA_SET_EBELL_I(ida, 2);
- IDA_SET_SUB_ADDR(ida, qcbp->paddr); /* physical address of this qcb */
- IDA_SET_SUB_LEN(ida, sizeof(qcb_t));
- IDA_SET_LBELL_I(ida, 1);
-
- qcbp->flags = QCB_SENT;
- ida->inside++;
- count++;
- }
-
- if (ida->inside > ida->max_inside) {
- ida->max_inside = ida->inside; /* new maximum */
- splx(opri);
-
-#ifdef IDADEBUG
- if (ida_debug & IDA_MAXQCBS)
- printf("ida%d: qcbs %d/%d\n", cntlr, ida->inside, ida->num_qcbs);
-#endif
- } else {
- splx(opri);
- }
-
-#ifdef IDADEBUG
- if ((ida_debug & IDA_SHOWSUBS) && count > 1)
- printf("ida%d: %d jobs submitted (queue %s).\n",
- cntlr, count, ida->send_next ? "not emptied" : "emptied");
-#endif
-}
-
-void
-ida_newqueue(int cntlr)
-{
- struct ida_ctl *ida = idadata[cntlr];
- ida->send_next = 0;
- ida->send_last = 0;
-}
-
-qcb_t *
-ida_dequeue(int cntlr)
-{
- struct ida_ctl *ida = idadata[cntlr];
- qcb_t *qcbp = ida->send_next; /* who is next? */
-
- if (qcbp) { /* queue is not empty */
- qcb_t* nextp = qcbp->next;
-
- if (nextp) { /* more than one element in the queue */
- nextp->last = 0; /* we are the first */
- ida->send_next = nextp; /* hence first to go */
- } else { /* exactly one element in the queue */
- ida->send_last = ida->send_next = 0;
- }
- }
-
- return qcbp;
-}
-
-static void
-ida_enqueue(int cntlr, qcb_t *qcbp)
-{
- struct ida_ctl *ida = idadata[cntlr];
- qcb_t *lastp = ida->send_last; /* who is last? */
- int opri = splbio();
-
- if (lastp) { /* if the queue is not empty */
- lastp->next = qcbp; /* then we go after the last */
- } else { /* if the queue was empty */
- ida->send_next = qcbp; /* then we go next */
- }
-
- qcbp->last = lastp; /* we follow the last */
- qcbp->next = 0; /* and nothing follows us */
-
- ida->send_last = qcbp; /* we go last */
- splx(opri);
-}
-
-void
-idaintr(void *arg)
-{
- int cntlr = (int)arg;
- qcb_t *qcbp;
- struct ida_ctl *ida = idadata[cntlr];
- u_char status;
- physaddr_t paddr, paddr1; /* physical address of the qcb */
- int offset; /* qcb offset */
- u_char cstat; /* job status */
-
- if (PCI_CONTROLLER(ida)) { /*pci:*/
- ida_pci_reg_t *reg = (ida_pci_reg_t *)ida->iobase;
- int status = reg->status;
-#ifdef IDADEBUG
- if (ida_debug & IDA_SHOWINTS)
- printf("ida%d: idaintr: status=%x (before complete)\n"
- , cntlr, status);
-#endif
- while (status & IDA_PCI_PENDING) {
- paddr1 = reg->complete_fifo;
- paddr = paddr1 & ~3;
- qcbp = ida_qcb_phys_kv(ida, paddr);
-#ifdef IDADEBUG
- if (ida_debug & IDA_SHOWQCBS) {
- printf("ida%d: idaintr: qcb(%x) completed\n", cntlr, qcbp);
- ida_print_qcb(qcbp);
- }
-#endif
- if (qcbp) {
- if (qcbp->req.error & 3) ida_soft_errors++;
- ida_done(cntlr, qcbp, (qcbp->req.error>>2) ? JOB_FAILURE : JOB_SUCCESS);
- } else {
- printf("ida%d: idaintr: completion (%x) ignored\n", cntlr, paddr1);
- }
- status = reg->status;
- }
-#ifdef IDADEBUG
- if (ida_debug & IDA_SHOWINTS)
- printf("ida%d: idaintr: status=%x (before initiate)\n"
- , cntlr, status);
-#endif
- if (status & IDA_PCI_READY) {
- ida_start(cntlr);
- }
- } else {
- while(1) {
- status = IDA_READ_EBELL_I(ida) & IDA_READ_EBELL_E(ida);
-
-#ifdef IDADEBUG
- if (ida_debug & IDA_SHOWINTS)
- printf("ida%d: idaintr: status = 0x%x\n", cntlr, status);
-#endif
-
- if ((status & (BMIC_DATA_READY | BMIC_CHAN_CLEAR)) == 0) break;
-
- if (status & BMIC_DATA_READY) { /* data ready */
- int job_status;
-
- if (IDA_READ_LBELL_I(ida) & JOB_ABORTED) {
- printf("ida%d: idaintr: status:%x local channel should be busy! ",
- cntlr, status);
- }
-
- paddr = IDA_GET_COM_ADDR(ida);
- offset = IDA_GET_COM_OFFSET(ida);
- cstat = IDA_GET_COM_STATUS(ida);
-
- /* acknowledge interrupt */
- IDA_ACK_CMD_COM(ida);
-
- /* determine which job completed */
- qcbp = ida_qcb_phys_kv(ida, paddr);
-
- /* analyse the job status code */
- if (cstat & IDA_COMPL_OK) {
- job_status = JOB_SUCCESS;
- } else {
- printf("ida%d: idaintr: return code %x=", cntlr, cstat);
- if (cstat & IDA_NON_FATAL) printf("recoverable error! ");
- if (cstat & IDA_FATAL) printf("fatal error! ");
- if (cstat & IDA_ABORTED) printf("aborted! ");
- if (cstat & IDA_INVAL_REQ) printf("invalid request block! ");
- if (cstat & IDA_INVAL_LIST) printf("cmd list error! ");
- if (cstat & IDA_AARGH_LIST) printf("really bad cmd list! ");
- job_status = JOB_FAILURE;
- }
-
-#ifdef IDADEBUG
- if (ida_debug & IDA_SHOWQCBS) {
- printf("ida%d: idaintr: qcb(%x) returned.\n", cntlr, qcbp);
- ida_print_qcb(qcbp);
- }
-#endif
-
- ida_done(cntlr, qcbp, job_status); /* retire the job */
- ida_start(cntlr); /* send the controller another job */
- }
-
- if (status & BMIC_CHAN_CLEAR) {
- /* channel not clear */
- IDA_DISABLE_CHAN(ida);
- ida_start(cntlr); /* send the controller another job */
- }
- } /*eisa*/
- }
-}
-
-
-int
-ida_poll(cntlr, wait)
-int cntlr;
-int wait; /* delay in milliseconds */
-{
- struct ida_ctl *ctlp = idadata[cntlr];
-
- if (PCI_CONTROLLER(ctlp)) {
- printf("ida%d: error: ida_poll called on a PCI controller\n", cntlr);
- return EIO;
- }
-
- while (wait-- > 0) {
- if (IDA_DATA_READY(ctlp)) {
- ctlp->com_addr = IDA_GET_COM_ADDR(ctlp);
- ctlp->com_offset = IDA_GET_COM_OFFSET(ctlp);
- ctlp->com_status = IDA_GET_COM_STATUS(ctlp);
- IDA_ACK_CMD_COM(ctlp);
-
- if (0) printf("ida_poll: addr=0x%08x off=0x%04x cmdstatus=0x%02x\n",
- (u_int)ctlp->com_addr,
- ctlp->com_offset,
- ctlp->com_status);
-
- return 0;
- }
- DELAY(1000);
- }
-
- printf("ida%d: board not responding\n", cntlr);
- return EIO;
-}
-
-
-int
-ida_submit(int cntlr, qcb_t *qcbp, int size)
-{
- struct ida_ctl *ida = idadata[cntlr];
- int s = splbio();
-
- if (PCI_CONTROLLER(ida)) {
- ida_pci_reg_t *reg = (ida_pci_reg_t *)ida->iobase;
- u_int fifo = reg->initiate_fifo;
- if (fifo == 1) {
- splx(s);
-#ifdef IDADEBUG
- if (ida_debug & IDA_SHOWSUBS)
- printf("ida%d: ida_submit(%x): fifo=1 not submitting\n",
- cntlr, qcbp, fifo);
-#endif
- return(1); /* not sent - must try again later */
- }
-#ifdef IDADEBUG
- if (ida_debug & IDA_SHOWSUBS)
- printf("ida%d: ida_submit(%x): fifo=%d submitting\n", cntlr, qcbp, fifo);
-#endif
- reg->initiate_fifo = qcbp->paddr;
-
- } else {
- if (!IDA_CHAN_CLEAR(ida)) {
- IDA_ENABLE_CHAN(ida);
- splx(s);
- return(1); /* not sent - must try again later */
- }
-
- IDA_SET_EBELL_I(ida, 2);
- IDA_SET_SUB_ADDR(ida, qcbp->paddr); /* physical address of this qcb */
- IDA_SET_SUB_LEN(ida, size);
- IDA_SET_LBELL_I(ida, 1);
- }
-
- splx(s);
- return(0); /* sent */
-}
-
-static
-void
-ida_empty_pci_complete_fifo(int cntlr, ida_pci_reg_t *reg) {
- u_long paddr;
- if (paddr = reg->complete_fifo) {
- int count = 200;
- while (paddr && count > 0) {
- printf("ida%d: command completion discarded (0x%x).\n",
- cntlr, (u_int)paddr);
- if (paddr = reg->complete_fifo) {
- DELAY(100);
- count--;
- }
- }
- }
- return;
-}
-
-static
-u_long
-ida_complete_pci_command(int cntlr, ida_pci_reg_t *reg) {
- int count = 1;
- u_long paddr;
- while (count < 1000000) {
- if (reg->status & IDA_PCI_PENDING) {
- if ((paddr = reg->complete_fifo) == 0)
- printf("ida%d: ida_complete_pci_command: zero address returned.\n",
- cntlr);
- else
- return paddr;
- }
- DELAY(10);
- }
- return 1;
-}
-
-static
-int
-ida_submit_wait(int cntlr, qcb_t *qcbp, int size)
-{
- struct ida_ctl *ida = idadata[cntlr];
-
- if (PCI_CONTROLLER(ida)) {
- ida_pci_reg_t *reg = (ida_pci_reg_t *)ida->iobase;
- int i, count = 1000000;
- u_long paddr;
- ida_empty_pci_complete_fifo(cntlr, reg);
- reg->interrupt = IDA_PCI_DISABLE_INTS;
- while (count > 0 && (i = reg->initiate_fifo) > 16) {
- DELAY(10);
- count--;
- }
- if (count == 0) {
- printf("ida%d: ida_pci_submit_wait: fifo failed to clear - controller has failed.\n", cntlr);
- return 1;
- }
- reg->initiate_fifo = qcbp->paddr;
- paddr = ida_complete_pci_command(cntlr, reg);
- if (paddr == 1) {
- printf("ida%d: ida_pci_submit_wait timeout. No command list returned.\n",
- cntlr);
- return 1;
- }
- if (paddr != qcbp->paddr) {
- printf("ida%d: ida_pci_submit_wait error. Invalid command list returned.\n",
- cntlr);
- return 1;
- }
- if (qcbp->req.error != 0xfe && qcbp->req.error == 0x40) {
- printf("ida%d: ida_pci_submit_wait: Job error.\n", cntlr);
- return 1;
- }
- } else {
- if (ida_submit(cntlr, qcbp, size)) {
- return 1;
- }
- if (ida_poll(cntlr, 10)) {
- return 1;
- }
- }
-
- return 0; /* sent */
-}
-
-
-void
-ida_done(int cntlr, qcb_t *qcbp, int state)
-{
- struct buf *bp = qcbp->buf;
-
- if (idadata[cntlr] > 0)
- idadata[cntlr]->inside--; /* one less job inside the controller */
-
- if (state != JOB_SUCCESS) {
-#ifdef IDADEBUG
- if (ida_debug & IDA_SHOWMISC)
- printf("ida%d: ida_done: job failed 0x%x\n", cntlr, state);
-#endif
- /* we had a problem */
- bp->b_error = EIO;
- bp->b_flags |= B_ERROR;
- } else {
- struct ida_drv *drv = id_drive[dkunit(bp->b_dev)];
- bp->b_resid = 0;
- /* Update device stats */
- devstat_end_transaction(&drv->dk_stats,
- bp->b_bcount - bp->b_resid,
- DEVSTAT_TAG_NONE,
- (bp->b_flags & B_READ) ? DEVSTAT_READ : DEVSTAT_WRITE);
- }
-
- ida_free_qcb(cntlr, qcbp);
- biodone(bp);
-}
-
-
-int
-idsize(dev_t dev)
-{
- int unit = dkunit(dev);
- struct ida_drv *drv;
-
- if (unit >= NID)
- return (-1);
-
- drv = id_drive[unit];
- if (!drv || !(drv->flags & ID_INIT))
- return (-1);
-
- return (dssize(dev, &drv->slices, idopen, idclose));
-}
-
-/*
- * dump all of physical memory into the partition specified, starting
- * at offset 'dumplo' into the partition.
- */
-int
-iddump(dev_t dev)
-{ /* dump core after a system crash */
- return 0; /* XXX */
-}
-
-static id_devsw_installed = 0;
-
-static void
-id_drvinit(void *unused)
-{
- if( ! id_devsw_installed ) {
- cdevsw_add(&id_cdevsw);
- id_devsw_installed = 1;
- }
-}
-
-SYSINIT(iddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+ID_CDMAJ,id_drvinit,NULL)
-
-#ifdef IDADEBUG
-void
-ida_print_qcb(qcb_t *qcbp)
-{
- int i;
- printf("qcb(%x): drive=%x priority=%x flags=%x sgcount=%d\n"
- ,qcbp
- ,qcbp->hdr.drive
- ,qcbp->hdr.priority
- ,qcbp->hdr.flags
- ,qcbp->req.sgcount);
- printf("qcb(%x): next=%x command=%x error=%x blkno=%x bcount=%x\n"
- ,qcbp
- ,qcbp->req.next
- ,qcbp->req.command
- ,qcbp->req.error
- ,qcbp->req.blkno
- ,qcbp->req.bcount);
- for (i=0; i < qcbp->req.sgcount; i++)
- printf("qcb(%x): %x len=%x addr=%x\n"
- ,qcbp
- ,i
- ,qcbp->sglist[i].len
- ,qcbp->sglist[i].addr);
-}
-
-void
-ida_print_active_qcb(int cntlr)
-{
- struct ida_ctl *ida = idadata[cntlr];
- int i;
-
- for (i=0; i < QCB_HASH_SIZE; i++) {
- qcb_t *qcbp = ida->hashlist[i];
- while (qcbp) {
- if (qcbp->flags != QCB_FREE) {
- ida_print_qcb(qcbp);
- }
- qcbp = qcbp->nexthash;
- }
- }
-}
-#endif /*IDADEBUG */
-
diff --git a/sys/pci/ida_pci.c b/sys/pci/ida_pci.c
new file mode 100644
index 0000000..95af154
--- /dev/null
+++ b/sys/pci/ida_pci.c
@@ -0,0 +1,199 @@
+/*-
+ * Copyright (c) 1999 Jonathan Lemon
+ * 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.
+ *
+ * $Id$
+ */
+
+#include <pci.h>
+#if NPCI > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+
+#include <sys/buf.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/devicestat.h>
+
+#include <machine/bus_memio.h>
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/rman.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+
+#include <dev/ida/idavar.h>
+
+#define IDA_PCI_MAX_DMA_ADDR 0xFFFFFFFF
+#define IDA_PCI_MAX_DMA_COUNT 0xFFFFFFFF
+
+#define IDA_PCI_MEMADDR (PCIR_MAPS + 4) /* Mem I/O Address */
+
+#define IDA_DEVICEID_SMART 0xAE100E11
+
+static struct {
+ u_long board;
+ char *desc;
+} board_id[] = {
+ { 0x4030, "Compaq SMART-2/P array controller" },
+ { 0x4031, "Compaq SMART-2SL array controller" },
+ { 0x4032, "Compaq Smart Array 3200 controller" },
+ { 0x4033, "Compaq Smart Array 3100ES controller" },
+ { 0x4034, "Compaq Smart Array 221 controller" },
+
+ { 0, "" },
+};
+
+static int ida_pci_probe(device_t dev);
+static int ida_pci_attach(device_t dev);
+static void ida_pci_print_child(device_t bus, device_t dev);
+
+static device_method_t ida_pci_methods[] = {
+ DEVMETHOD(device_probe, ida_pci_probe),
+ DEVMETHOD(device_attach, ida_pci_attach),
+
+ DEVMETHOD(bus_print_child, ida_pci_print_child),
+
+ { 0, 0 }
+};
+
+static driver_t ida_pci_driver = {
+ "ida",
+ ida_pci_methods,
+ sizeof(struct ida_softc)
+};
+
+static devclass_t ida_devclass;
+
+static int
+ida_pci_probe(device_t dev)
+{
+ u_long board;
+ int i;
+
+ if (pci_get_devid(dev) == IDA_DEVICEID_SMART) {
+ board = pci_get_subdevice(dev);
+ for (i = 0; board_id[i].board; i++) {
+ if (board_id[i].board == board) {
+ device_set_desc(dev, board_id[i].desc);
+ return (0);
+ }
+ }
+ /*
+ * It's an unknown Compaq SMART device, but assume we
+ * can support it.
+ */
+ device_set_desc(dev, "Unknown Compaq Smart Array controller");
+ return (0);
+ }
+ return (ENXIO);
+}
+
+static int
+ida_pci_attach(device_t dev)
+{
+ struct ida_softc *ida;
+ u_int command;
+ int error, rid;
+
+ command = pci_read_config(dev, PCIR_COMMAND, 1);
+
+ /*
+ * for multiple card types, need to re-determine which type is
+ * being attached here
+ */
+
+ /*
+ * it appears that this board only does MEMIO access.
+ */
+ if ((command & PCIM_CMD_MEMEN) == 0) {
+ device_printf(dev, "Only memory mapped I/O is supported\n");
+ return (ENXIO);
+ }
+
+ ida = (struct ida_softc *)device_get_softc(dev);
+ ida->dev = dev;
+
+ ida->regs_res_type = SYS_RES_MEMORY;
+ ida->regs_res_id = IDA_PCI_MEMADDR;
+ ida->regs = bus_alloc_resource(dev, ida->regs_res_type,
+ &ida->regs_res_id, 0, ~0, 1, RF_ACTIVE);
+ if (ida->regs == NULL) {
+ device_printf(dev, "can't allocate register resources\n");
+ return (ENOMEM);
+ }
+
+ error = bus_dma_tag_create(/*parent*/NULL, /*alignment*/0,
+ /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/MAXBSIZE, /*nsegments*/IDA_NSEG,
+ /*maxsegsize*/BUS_SPACE_MAXSIZE_32BIT, /*flags*/BUS_DMA_ALLOCNOW,
+ &ida->parent_dmat);
+ if (error != 0) {
+ device_printf(dev, "can't allocate DMA tag\n");
+ ida_free(ida);
+ return (ENOMEM);
+ }
+
+ rid = 0;
+ ida->irq_res_type = SYS_RES_IRQ;
+ ida->irq = bus_alloc_resource(dev, ida->irq_res_type, &rid,
+ 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
+ if (ida->irq == NULL) {
+ ida_free(ida);
+ return (ENOMEM);
+ }
+ error = bus_setup_intr(dev, ida->irq, INTR_TYPE_BIO,
+ ida_intr, ida, &ida->ih);
+ if (error) {
+ device_printf(dev, "can't setup interrupt\n");
+ ida_free(ida);
+ return (ENOMEM);
+ }
+
+ error = ida_init(ida);
+ if (error) {
+ ida_free(ida);
+ return (error);
+ }
+ ida_attach(ida);
+ ida->flags = IDA_ATTACHED;
+
+ return (0);
+}
+
+static void
+ida_pci_print_child(device_t bus, device_t dev)
+{
+ printf(" on %s%d", device_get_name(bus), device_get_unit(bus));
+}
+
+DRIVER_MODULE(ida, pci, ida_pci_driver, ida_devclass, 0, 0);
+
+#endif /* NPCI > 0 */
OpenPOWER on IntegriCloud