Module Name:    src
Committed By:   jdolecek
Date:           Sun Dec  3 14:26:38 UTC 2017

Modified Files:
        src/sys/dev/pci: files.pci
Added Files:
        src/sys/dev/pci: ips.c

Log Message:
port ips(4) driver from OpenBSD; needs a lot more work, right now just 
compilable


To generate a diff of this commit:
cvs rdiff -u -r1.391 -r1.392 src/sys/dev/pci/files.pci
cvs rdiff -u -r0 -r1.1 src/sys/dev/pci/ips.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/pci/files.pci
diff -u src/sys/dev/pci/files.pci:1.391 src/sys/dev/pci/files.pci:1.392
--- src/sys/dev/pci/files.pci:1.391	Tue Sep  5 08:01:43 2017
+++ src/sys/dev/pci/files.pci	Sun Dec  3 14:26:38 2017
@@ -1,4 +1,4 @@
-#	$NetBSD: files.pci,v 1.391 2017/09/05 08:01:43 skrll Exp $
+#	$NetBSD: files.pci,v 1.392 2017/12/03 14:26:38 jdolecek Exp $
 #
 # Config file and device description for machine-independent PCI code.
 # Included by ports that need it.  Requires that the SCSI files be
@@ -107,6 +107,11 @@ file	dev/pci/icp_pci.c		icp_pci
 attach	aac at pci with aac_pci
 file	dev/pci/aac_pci.c		aac_pci
 
+# IBM ServeRAID RAID controllers
+device  ips: scsi
+attach  ips at pci
+file    dev/pci/ips.c                   ips
+
 # DPT EATA SCSI controllers
 attach	dpt at pci with dpt_pci
 file	dev/pci/dpt_pci.c		dpt_pci

Added files:

Index: src/sys/dev/pci/ips.c
diff -u /dev/null src/sys/dev/pci/ips.c:1.1
--- /dev/null	Sun Dec  3 14:26:38 2017
+++ src/sys/dev/pci/ips.c	Sun Dec  3 14:26:38 2017
@@ -0,0 +1,2013 @@
+/*	$NetBSD: ips.c,v 1.1 2017/12/03 14:26:38 jdolecek Exp $	*/
+/*	$OpenBSD: ips.c,v 1.113 2016/08/14 04:08:03 dlg Exp $	*/
+
+/*-
+ * Copyright (c) 2017 The NetBSD Foundation, Inc.
+ * 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 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) 2006, 2007, 2009 Alexander Yurchenko <gra...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * IBM (Adaptec) ServeRAID controllers driver.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: ips.c,v 1.1 2017/12/03 14:26:38 jdolecek Exp $");
+
+#include "bio.h"
+
+#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/kthread.h>
+
+#include <sys/bus.h>
+#include <sys/intr.h>
+
+#include <dev/scsipi/scsi_all.h>
+#include <dev/scsipi/scsipi_all.h>
+#include <dev/scsipi/scsi_disk.h>
+#include <dev/scsipi/scsipi_disk.h>
+#include <dev/scsipi/scsiconf.h>
+
+#include <dev/biovar.h>
+#include <dev/sysmon/sysmonvar.h>
+#include <sys/envsys.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+
+/* Debug levels */
+#define IPS_D_ERR	0x0001	/* errors */
+#define IPS_D_INFO	0x0002	/* information */
+#define IPS_D_XFER	0x0004	/* transfers */
+
+#ifdef IPS_DEBUG
+#define DPRINTF(a, b)	do { if (ips_debug & (a)) printf b; } while (0)
+int ips_debug = IPS_D_ERR;
+#else
+#define DPRINTF(a, b)
+#endif
+
+#define IPS_MAXDRIVES		8
+#define IPS_MAXCHANS		4
+#define IPS_MAXTARGETS		16
+#define IPS_MAXCHUNKS		16
+#define IPS_MAXCMDS		128
+
+#define IPS_MAXFER		(64 * 1024)
+#define IPS_MAXSGS		16
+#define IPS_MAXCDB		12
+
+#define IPS_SECSZ		512
+#define IPS_NVRAMPGSZ		128
+#define IPS_SQSZ		(IPS_MAXCMDS * sizeof(u_int32_t))
+
+#define	IPS_TIMEOUT		60000	/* ms */
+
+/* Command codes */
+#define IPS_CMD_READ		0x02
+#define IPS_CMD_WRITE		0x03
+#define IPS_CMD_DCDB		0x04
+#define IPS_CMD_GETADAPTERINFO	0x05
+#define IPS_CMD_FLUSH		0x0a
+#define IPS_CMD_REBUILDSTATUS	0x0c
+#define IPS_CMD_SETSTATE	0x10
+#define IPS_CMD_REBUILD		0x16
+#define IPS_CMD_ERRORTABLE	0x17
+#define IPS_CMD_GETDRIVEINFO	0x19
+#define IPS_CMD_RESETCHAN	0x1a
+#define IPS_CMD_DOWNLOAD	0x20
+#define IPS_CMD_RWBIOSFW	0x22
+#define IPS_CMD_READCONF	0x38
+#define IPS_CMD_GETSUBSYS	0x40
+#define IPS_CMD_CONFIGSYNC	0x58
+#define IPS_CMD_READ_SG		0x82
+#define IPS_CMD_WRITE_SG	0x83
+#define IPS_CMD_DCDB_SG		0x84
+#define IPS_CMD_EDCDB		0x95
+#define IPS_CMD_EDCDB_SG	0x96
+#define IPS_CMD_RWNVRAMPAGE	0xbc
+#define IPS_CMD_GETVERINFO	0xc6
+#define IPS_CMD_FFDC		0xd7
+#define IPS_CMD_SG		0x80
+#define IPS_CMD_RWNVRAM		0xbc
+
+/* DCDB attributes */
+#define IPS_DCDB_DATAIN		0x01	/* data input */
+#define IPS_DCDB_DATAOUT	0x02	/* data output */
+#define IPS_DCDB_XFER64K	0x08	/* 64K transfer */
+#define IPS_DCDB_TIMO10		0x10	/* 10 secs timeout */
+#define IPS_DCDB_TIMO60		0x20	/* 60 secs timeout */
+#define IPS_DCDB_TIMO20M	0x30	/* 20 mins timeout */
+#define IPS_DCDB_NOAUTOREQSEN	0x40	/* no auto request sense */
+#define IPS_DCDB_DISCON		0x80	/* disconnect allowed */
+
+/* Register definitions */
+#define IPS_REG_HIS		0x08	/* host interrupt status */
+#define IPS_REG_HIS_SCE			0x01	/* status channel enqueue */
+#define IPS_REG_HIS_EN			0x80	/* enable interrupts */
+#define IPS_REG_CCSA		0x10	/* command channel system address */
+#define IPS_REG_CCC		0x14	/* command channel control */
+#define IPS_REG_CCC_SEM			0x0008	/* semaphore */
+#define IPS_REG_CCC_START		0x101a	/* start command */
+#define IPS_REG_SQH		0x20	/* status queue head */
+#define IPS_REG_SQT		0x24	/* status queue tail */
+#define IPS_REG_SQE		0x28	/* status queue end */
+#define IPS_REG_SQS		0x2c	/* status queue start */
+
+#define IPS_REG_OIS		0x30	/* outbound interrupt status */
+#define IPS_REG_OIS_PEND		0x0008	/* interrupt is pending */
+#define IPS_REG_OIM		0x34	/* outbound interrupt mask */
+#define IPS_REG_OIM_DS			0x0008	/* disable interrupts */
+#define IPS_REG_IQP		0x40	/* inbound queue port */
+#define IPS_REG_OQP		0x44	/* outbound queue port */
+
+/* Status word fields */
+#define IPS_STAT_ID(x)		(((x) >> 8) & 0xff)	/* command id */
+#define IPS_STAT_BASIC(x)	(((x) >> 16) & 0xff)	/* basic status */
+#define IPS_STAT_EXT(x)		(((x) >> 24) & 0xff)	/* ext status */
+#define IPS_STAT_GSC(x)		((x) & 0x0f)
+
+/* Basic status codes */
+#define IPS_STAT_OK		0x00	/* success */
+#define IPS_STAT_RECOV		0x01	/* recovered error */
+#define IPS_STAT_INVOP		0x03	/* invalid opcode */
+#define IPS_STAT_INVCMD		0x04	/* invalid command block */
+#define IPS_STAT_INVPARM	0x05	/* invalid parameters block */
+#define IPS_STAT_BUSY		0x08	/* busy */
+#define IPS_STAT_CMPLERR	0x0c	/* completed with error */
+#define IPS_STAT_LDERR		0x0d	/* logical drive error */
+#define IPS_STAT_TIMO		0x0e	/* timeout */
+#define IPS_STAT_PDRVERR	0x0f	/* physical drive error */
+
+/* Extended status codes */
+#define IPS_ESTAT_SELTIMO	0xf0	/* select timeout */
+#define IPS_ESTAT_OURUN		0xf2	/* over/underrun */
+#define IPS_ESTAT_HOSTRST	0xf7	/* host reset */
+#define IPS_ESTAT_DEVRST	0xf8	/* device reset */
+#define IPS_ESTAT_RECOV		0xfc	/* recovered error */
+#define IPS_ESTAT_CKCOND	0xff	/* check condition */
+
+#define IPS_IOSIZE		128	/* max space size to map */
+
+/* Command frame */
+struct ips_cmd {
+	u_int8_t	code;
+	u_int8_t	id;
+	u_int8_t	drive;
+	u_int8_t	sgcnt;
+	u_int32_t	lba;
+	u_int32_t	sgaddr;
+	u_int16_t	seccnt;
+	u_int8_t	seg4g;
+	u_int8_t	esg;
+	u_int32_t	ccsar;
+	u_int32_t	cccr;
+};
+
+/* Direct CDB (SCSI pass-through) frame */
+struct ips_dcdb {
+	u_int8_t	device;
+	u_int8_t	attr;
+	u_int16_t	datalen;
+	u_int32_t	sgaddr;
+	u_int8_t	cdblen;
+	u_int8_t	senselen;
+	u_int8_t	sgcnt;
+	u_int8_t	__reserved1;
+	u_int8_t	cdb[IPS_MAXCDB];
+	u_int8_t	sense[64];
+	u_int8_t	status;
+	u_int8_t	__reserved2[3];
+};
+
+/* Scatter-gather array element */
+struct ips_sg {
+	u_int32_t	addr;
+	u_int32_t	size;
+};
+
+/* Command block */
+struct ips_cmdb {
+	struct ips_cmd	cmd;
+	struct ips_dcdb	dcdb;
+	struct ips_sg	sg[IPS_MAXSGS];
+};
+
+/* Data frames */
+struct ips_adapterinfo {
+	u_int8_t	drivecnt;
+	u_int8_t	miscflag;
+	u_int8_t	sltflag;
+	u_int8_t	bstflag;
+	u_int8_t	pwrchgcnt;
+	u_int8_t	wrongaddrcnt;
+	u_int8_t	unidentcnt;
+	u_int8_t	nvramdevchgcnt;
+	u_int8_t	firmware[8];
+	u_int8_t	bios[8];
+	u_int32_t	drivesize[IPS_MAXDRIVES];
+	u_int8_t	cmdcnt;
+	u_int8_t	maxphysdevs;
+	u_int16_t	flashrepgmcnt;
+	u_int8_t	defunctdiskcnt;
+	u_int8_t	rebuildflag;
+	u_int8_t	offdrivecnt;
+	u_int8_t	critdrivecnt;
+	u_int16_t	confupdcnt;
+	u_int8_t	blkflag;
+	u_int8_t	__reserved;
+	u_int16_t	deaddisk[IPS_MAXCHANS][IPS_MAXTARGETS];
+};
+
+struct ips_driveinfo {
+	u_int8_t	drivecnt;
+	u_int8_t	__reserved[3];
+	struct ips_drive {
+		u_int8_t	id;
+		u_int8_t	__reserved;
+		u_int8_t	raid;
+		u_int8_t	state;
+#define IPS_DS_FREE	0x00
+#define IPS_DS_OFFLINE	0x02
+#define IPS_DS_ONLINE	0x03
+#define IPS_DS_DEGRADED	0x04
+#define IPS_DS_SYS	0x06
+#define IPS_DS_CRS	0x24
+
+		u_int32_t	seccnt;
+	}		drive[IPS_MAXDRIVES];
+};
+
+struct ips_conf {
+	u_int8_t	ldcnt;
+	u_int8_t	day;
+	u_int8_t	month;
+	u_int8_t	year;
+	u_int8_t	initid[4];
+	u_int8_t	hostid[12];
+	u_int8_t	time[8];
+	u_int32_t	useropt;
+	u_int16_t	userfield;
+	u_int8_t	rebuildrate;
+	u_int8_t	__reserved1;
+
+	struct ips_hw {
+		u_int8_t	board[8];
+		u_int8_t	cpu[8];
+		u_int8_t	nchantype;
+		u_int8_t	nhostinttype;
+		u_int8_t	compression;
+		u_int8_t	nvramtype;
+		u_int32_t	nvramsize;
+	}		hw;
+
+	struct ips_ld {
+		u_int16_t	userfield;
+		u_int8_t	state;
+		u_int8_t	raidcacheparam;
+		u_int8_t	chunkcnt;
+		u_int8_t	stripesize;
+		u_int8_t	params;
+		u_int8_t	__reserved;
+		u_int32_t	size;
+
+		struct ips_chunk {
+			u_int8_t	channel;
+			u_int8_t	target;
+			u_int16_t	__reserved;
+			u_int32_t	startsec;
+			u_int32_t	seccnt;
+		}		chunk[IPS_MAXCHUNKS];
+	}		ld[IPS_MAXDRIVES];
+
+	struct ips_dev {
+		u_int8_t	initiator;
+		u_int8_t	params;
+		u_int8_t	miscflag;
+		u_int8_t	state;
+#define IPS_DVS_STANDBY	0x01
+#define IPS_DVS_REBUILD	0x02
+#define IPS_DVS_SPARE	0x04
+#define IPS_DVS_MEMBER	0x08
+#define IPS_DVS_ONLINE	0x80
+#define IPS_DVS_READY	(IPS_DVS_STANDBY | IPS_DVS_ONLINE)
+
+		u_int32_t	seccnt;
+		u_int8_t	devid[28];
+	}		dev[IPS_MAXCHANS][IPS_MAXTARGETS];
+
+	u_int8_t	reserved[512];
+};
+
+struct ips_rblstat {
+	u_int8_t	__unknown[20];
+	struct {
+		u_int8_t	__unknown[4];
+		u_int32_t	total;
+		u_int32_t	remain;
+	}		ld[IPS_MAXDRIVES];
+};
+
+struct ips_pg5 {
+	u_int32_t	signature;
+	u_int8_t	__reserved1;
+	u_int8_t	slot;
+	u_int16_t	type;
+	u_int8_t	bioshi[4];
+	u_int8_t	bioslo[4];
+	u_int16_t	__reserved2;
+	u_int8_t	__reserved3;
+	u_int8_t	os;
+	u_int8_t	driverhi[4];
+	u_int8_t	driverlo[4];
+	u_int8_t	__reserved4[100];
+};
+
+struct ips_info {
+	struct ips_adapterinfo	adapter;
+	struct ips_driveinfo	drive;
+	struct ips_conf		conf;
+	struct ips_rblstat	rblstat;
+	struct ips_pg5		pg5;
+};
+
+/* Command control block */
+struct ips_softc;
+struct ips_ccb {
+	struct ips_softc *	c_sc;		/* driver softc */
+	int			c_id;		/* command id */
+	int			c_flags;	/* SCSI_* flags */
+	enum {
+		IPS_CCB_FREE,
+		IPS_CCB_QUEUED,
+		IPS_CCB_DONE
+	}			c_state;	/* command state */
+
+	void *			c_cmdbva;	/* command block virt addr */
+	paddr_t			c_cmdbpa;	/* command block phys addr */
+	bus_dmamap_t		c_dmam;		/* data buffer DMA map */
+
+	struct scsipi_xfer *	c_xfer;		/* corresponding SCSI xfer */
+
+	u_int8_t		c_stat;		/* status byte copy */
+	u_int8_t		c_estat;	/* ext status byte copy */
+	int			c_error;	/* completion error */
+
+	void			(*c_done)(struct ips_softc *,	/* cmd done */
+				    struct ips_ccb *);		/* callback */
+
+	SLIST_ENTRY(ips_ccb)	c_link;		/* queue link */
+};
+
+/* CCB queue */
+SLIST_HEAD(ips_ccbq, ips_ccb);
+
+/* DMA-able chunk of memory */
+struct dmamem {
+	bus_dma_tag_t		dm_tag;
+	bus_dmamap_t		dm_map;
+	bus_dma_segment_t	dm_seg;
+	bus_size_t		dm_size;
+	void *			dm_vaddr;
+#define dm_paddr dm_seg.ds_addr
+};
+
+struct ips_softc {
+	struct device		sc_dev;
+
+	/* SCSI mid-layer connection. */
+	struct scsipi_adapter   sc_adapt;
+
+	struct ips_pt {
+		struct scsipi_channel	pt_chan;
+		int			pt_nchan;
+		struct ips_softc *	pt_sc;
+
+		int			pt_proctgt;
+		char			pt_procdev[16];
+	}			sc_pt[IPS_MAXCHANS];
+
+	bus_space_tag_t		sc_iot;
+	bus_space_handle_t	sc_ioh;
+	bus_dma_tag_t		sc_dmat;
+
+	const struct ips_chipset *sc_chip;
+
+	struct ips_info *	sc_info;
+	struct dmamem		sc_infom;
+
+	int			sc_nunits;
+
+	struct dmamem		sc_cmdbm;
+
+	struct ips_ccb *	sc_ccb;
+	int			sc_nccbs;
+	struct ips_ccbq		sc_ccbq_free;
+	struct kmutex		sc_ccb_mtx;
+
+	struct dmamem		sc_sqm;
+	paddr_t			sc_sqtail;
+	u_int32_t *		sc_sqbuf;
+	int			sc_sqidx;
+};
+
+int	ips_match(device_t, cfdata_t, void *);
+void	ips_attach(struct device *, struct device *, void *);
+
+void	ips_scsi_cmd(struct ips_ccb *);
+void	ips_scsi_pt_cmd(struct scsipi_xfer *);
+static void ips_scsipi_request(struct scsipi_channel *,
+	    scsipi_adapter_req_t, void *);
+int	ips_scsi_ioctl(struct scsipi_channel *, u_long, void *,
+	    int, struct proc *);
+
+#if NBIO > 0
+int	ips_ioctl(device_t, u_long, void *);
+int	ips_ioctl_inq(struct ips_softc *, struct bioc_inq *);
+int	ips_ioctl_vol(struct ips_softc *, struct bioc_vol *);
+int	ips_ioctl_disk(struct ips_softc *, struct bioc_disk *);
+int	ips_ioctl_setstate(struct ips_softc *, struct bioc_setstate *);
+#endif
+
+int	ips_load_xs(struct ips_softc *, struct ips_ccb *, struct scsipi_xfer *);
+void	ips_start_xs(struct ips_softc *, struct ips_ccb *, struct scsipi_xfer *);
+
+int	ips_cmd(struct ips_softc *, struct ips_ccb *);
+int	ips_poll(struct ips_softc *, struct ips_ccb *);
+void	ips_done(struct ips_softc *, struct ips_ccb *);
+void	ips_done_xs(struct ips_softc *, struct ips_ccb *);
+void	ips_done_pt(struct ips_softc *, struct ips_ccb *);
+void	ips_done_mgmt(struct ips_softc *, struct ips_ccb *);
+int	ips_error(struct ips_softc *, struct ips_ccb *);
+int	ips_error_xs(struct ips_softc *, struct ips_ccb *);
+int	ips_intr(void *);
+void	ips_timeout(void *);
+
+int	ips_getadapterinfo(struct ips_softc *, int);
+int	ips_getdriveinfo(struct ips_softc *, int);
+int	ips_getconf(struct ips_softc *, int);
+int	ips_getpg5(struct ips_softc *, int);
+
+#if NBIO > 0
+int	ips_getrblstat(struct ips_softc *, int);
+int	ips_setstate(struct ips_softc *, int, int, int, int);
+int	ips_rebuild(struct ips_softc *, int, int, int, int, int);
+#endif
+
+void	ips_copperhead_exec(struct ips_softc *, struct ips_ccb *);
+void	ips_copperhead_intren(struct ips_softc *);
+int	ips_copperhead_isintr(struct ips_softc *);
+u_int32_t ips_copperhead_status(struct ips_softc *);
+
+void	ips_morpheus_exec(struct ips_softc *, struct ips_ccb *);
+void	ips_morpheus_intren(struct ips_softc *);
+int	ips_morpheus_isintr(struct ips_softc *);
+u_int32_t ips_morpheus_status(struct ips_softc *);
+
+struct ips_ccb *ips_ccb_alloc(struct ips_softc *, int);
+void	ips_ccb_free(struct ips_softc *, struct ips_ccb *, int);
+struct ips_ccb *ips_ccb_get(struct ips_softc *);
+void	ips_ccb_put(struct ips_softc *, struct ips_ccb *);
+
+int	ips_dmamem_alloc(struct dmamem *, bus_dma_tag_t, bus_size_t);
+void	ips_dmamem_free(struct dmamem *);
+
+extern struct  cfdriver ips_cd;
+
+CFATTACH_DECL_NEW(ips, sizeof(struct ips_softc),
+    ips_match, ips_attach, NULL, NULL);
+
+static struct ips_ident {
+        pci_vendor_id_t vendor;
+        pci_product_id_t product;
+} const ips_ids[] = {
+	{ PCI_VENDOR_IBM,	PCI_PRODUCT_IBM_SERVERAID },
+	{ PCI_VENDOR_IBM,	PCI_PRODUCT_IBM_SERVERAID4 },
+	{ PCI_VENDOR_ADP2,	PCI_PRODUCT_ADP2_SERVERAID }
+};
+
+static const struct ips_chipset {
+	enum {
+		IPS_CHIP_COPPERHEAD = 0,
+		IPS_CHIP_MORPHEUS
+	}		ic_id;
+
+	int		ic_bar;
+
+	void		(*ic_exec)(struct ips_softc *, struct ips_ccb *);
+	void		(*ic_intren)(struct ips_softc *);
+	int		(*ic_isintr)(struct ips_softc *);
+	u_int32_t	(*ic_status)(struct ips_softc *);
+} ips_chips[] = {
+	{
+		IPS_CHIP_COPPERHEAD,
+		0x14,
+		ips_copperhead_exec,
+		ips_copperhead_intren,
+		ips_copperhead_isintr,
+		ips_copperhead_status
+	},
+	{
+		IPS_CHIP_MORPHEUS,
+		0x10,
+		ips_morpheus_exec,
+		ips_morpheus_intren,
+		ips_morpheus_isintr,
+		ips_morpheus_status
+	}
+};
+
+#define ips_exec(s, c)	(s)->sc_chip->ic_exec((s), (c))
+#define ips_intren(s)	(s)->sc_chip->ic_intren((s))
+#define ips_isintr(s)	(s)->sc_chip->ic_isintr((s))
+#define ips_status(s)	(s)->sc_chip->ic_status((s))
+
+static const char *ips_names[] = {
+	NULL,
+	NULL,
+	"II",
+	"onboard",
+	"onboard",
+	"3H",
+	"3L",
+	"4H",
+	"4M",
+	"4L",
+	"4Mx",
+	"4Lx",
+	"5i",
+	"5i",
+	"6M",
+	"6i",
+	"7t",
+	"7k",
+	"7M"
+};
+
+/* Lookup supported device table */
+static const struct ips_ident *
+ips_lookup(const struct pci_attach_args *pa)
+{
+        const struct ips_ident *imp;
+	int i;
+
+	for (i = 0, imp = ips_ids; i < __arraycount(ips_ids); i++, imp++) {
+                if (PCI_VENDOR(pa->pa_id) == imp->vendor &&
+                    PCI_PRODUCT(pa->pa_id) == imp->product)
+                        return imp;
+        }
+        return NULL;
+}
+
+int
+ips_match(device_t parent, cfdata_t cfdata, void *aux)
+{
+	struct pci_attach_args *pa = aux;
+
+	if (ips_lookup(pa) != NULL)
+		return 1;
+
+	return 0;
+}
+
+void
+ips_attach(struct device *parent, struct device *self, void *aux)
+{
+	struct ips_softc *sc = (struct ips_softc *)self;
+	struct pci_attach_args *pa = aux;
+	struct ips_ccb ccb0;
+	struct ips_adapterinfo *ai;
+	struct ips_driveinfo *di;
+	struct ips_pg5 *pg5;
+	pcireg_t maptype;
+	bus_size_t iosize;
+	pci_intr_handle_t ih;
+	const char *intrstr;
+	int type, i;
+	struct scsipi_adapter *adapt;
+	struct scsipi_channel *chan;
+	char intrbuf[PCI_INTRSTR_LEN];
+
+	sc->sc_dmat = pa->pa_dmat;
+
+	/* Identify chipset */
+	if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_IBM_SERVERAID)
+		sc->sc_chip = &ips_chips[IPS_CHIP_COPPERHEAD];
+	else
+		sc->sc_chip = &ips_chips[IPS_CHIP_MORPHEUS];
+
+	/* Map registers */
+	// XXX check IPS_IOSIZE as old code used to do?
+	maptype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, sc->sc_chip->ic_bar);
+	if (pci_mapreg_map(pa, sc->sc_chip->ic_bar, maptype, 0, &sc->sc_iot,
+	    &sc->sc_ioh, NULL, &iosize)) {
+		printf(": can't map regs\n");
+		return;
+	}
+
+	/* Allocate command buffer */
+	if (ips_dmamem_alloc(&sc->sc_cmdbm, sc->sc_dmat,
+	    IPS_MAXCMDS * sizeof(struct ips_cmdb))) {
+		printf(": can't alloc cmd buffer\n");
+		goto fail1;
+	}
+
+	/* Allocate info buffer */
+	if (ips_dmamem_alloc(&sc->sc_infom, sc->sc_dmat,
+	    sizeof(struct ips_info))) {
+		printf(": can't alloc info buffer\n");
+		goto fail2;
+	}
+	sc->sc_info = sc->sc_infom.dm_vaddr;
+	ai = &sc->sc_info->adapter;
+	di = &sc->sc_info->drive;
+	pg5 = &sc->sc_info->pg5;
+
+	/* Allocate status queue for the Copperhead chipset */
+	if (sc->sc_chip->ic_id == IPS_CHIP_COPPERHEAD) {
+		if (ips_dmamem_alloc(&sc->sc_sqm, sc->sc_dmat, IPS_SQSZ)) {
+			printf(": can't alloc status queue\n");
+			goto fail3;
+		}
+		sc->sc_sqtail = sc->sc_sqm.dm_paddr;
+		sc->sc_sqbuf = sc->sc_sqm.dm_vaddr;
+		sc->sc_sqidx = 0;
+		bus_space_write_4(sc->sc_iot, sc->sc_ioh, IPS_REG_SQS,
+		    sc->sc_sqm.dm_paddr);
+		bus_space_write_4(sc->sc_iot, sc->sc_ioh, IPS_REG_SQE,
+		    sc->sc_sqm.dm_paddr + IPS_SQSZ);
+		bus_space_write_4(sc->sc_iot, sc->sc_ioh, IPS_REG_SQH,
+		    sc->sc_sqm.dm_paddr + sizeof(u_int32_t));
+		bus_space_write_4(sc->sc_iot, sc->sc_ioh, IPS_REG_SQT,
+		    sc->sc_sqm.dm_paddr);
+	}
+
+	/* Bootstrap CCB queue */
+	sc->sc_nccbs = 1;
+	sc->sc_ccb = &ccb0;
+	bzero(&ccb0, sizeof(ccb0));
+	ccb0.c_cmdbva = sc->sc_cmdbm.dm_vaddr;
+	ccb0.c_cmdbpa = sc->sc_cmdbm.dm_paddr;
+	SLIST_INIT(&sc->sc_ccbq_free);
+	SLIST_INSERT_HEAD(&sc->sc_ccbq_free, &ccb0, c_link);
+	mutex_init(&sc->sc_ccb_mtx, MUTEX_DEFAULT, IPL_BIO);
+
+	/* Get adapter info */
+	if (ips_getadapterinfo(sc, XS_CTL_NOSLEEP)) {
+		printf(": can't get adapter info\n");
+		goto fail4;
+	}
+
+	/* Get logical drives info */
+	if (ips_getdriveinfo(sc, XS_CTL_NOSLEEP)) {
+		printf(": can't get ld info\n");
+		goto fail4;
+	}
+	sc->sc_nunits = di->drivecnt;
+
+	/* Get configuration */
+	if (ips_getconf(sc, XS_CTL_NOSLEEP)) {
+		printf(": can't get config\n");
+		goto fail4;
+	}
+
+	/* Read NVRAM page 5 for additional info */
+	(void)ips_getpg5(sc, XS_CTL_NOSLEEP);
+
+	/* Initialize CCB queue */
+	sc->sc_nccbs = ai->cmdcnt;
+	if ((sc->sc_ccb = ips_ccb_alloc(sc, sc->sc_nccbs)) == NULL) {
+		printf(": can't alloc ccb queue\n");
+		goto fail4;
+	}
+	SLIST_INIT(&sc->sc_ccbq_free);
+	for (i = 0; i < sc->sc_nccbs; i++)
+		SLIST_INSERT_HEAD(&sc->sc_ccbq_free,
+		    &sc->sc_ccb[i], c_link);
+
+	/* Install interrupt handler */
+	if (pci_intr_map(pa, &ih)) {
+		printf(": can't map interrupt\n");
+		goto fail5;
+	}
+	intrstr = pci_intr_string(pa->pa_pc, ih, intrbuf, sizeof(intrbuf));
+	if (pci_intr_establish_xname(pa->pa_pc, ih, IPL_BIO, ips_intr, sc,
+	    sc->sc_dev.dv_xname) == NULL) {
+		printf(": can't establish interrupt");
+		if (intrstr != NULL)
+			printf(" at %s", intrstr);
+		printf("\n");
+		goto fail5;
+	}
+	printf(": %s\n", intrstr);
+
+	/* Display adapter info */
+	printf("%s: ServeRAID", sc->sc_dev.dv_xname);
+	type = htole16(pg5->type);
+	if (type < sizeof(ips_names) / sizeof(ips_names[0]) && ips_names[type])
+		printf(" %s", ips_names[type]);
+	printf(", FW %c%c%c%c%c%c%c", ai->firmware[0], ai->firmware[1],
+	    ai->firmware[2], ai->firmware[3], ai->firmware[4], ai->firmware[5],
+	    ai->firmware[6]);
+	printf(", BIOS %c%c%c%c%c%c%c", ai->bios[0], ai->bios[1], ai->bios[2],
+	    ai->bios[3], ai->bios[4], ai->bios[5], ai->bios[6]);
+	printf(", %d cmds, %d LD%s", sc->sc_nccbs, sc->sc_nunits,
+	    (sc->sc_nunits == 1 ? "" : "s"));
+	printf("\n");
+
+	/*
+	 * Attach to scsipi.
+	 */
+	adapt = &sc->sc_adapt;
+	memset(adapt, 0, sizeof(*adapt));
+	adapt->adapt_dev = self;
+	adapt->adapt_nchannels = IPS_MAXCHANS;
+	if (sc->sc_nunits > 0)
+		adapt->adapt_openings = sc->sc_nccbs / sc->sc_nunits;
+	adapt->adapt_max_periph = adapt->adapt_openings;
+	adapt->adapt_request = ips_scsipi_request;
+	adapt->adapt_minphys = minphys;
+	adapt->adapt_ioctl = ips_scsi_ioctl;
+
+	/* For each channel attach SCSI pass-through bus */
+	for (i = 0; i < IPS_MAXCHANS; i++) {
+		struct ips_pt *pt;
+		int target, lastarget;
+
+		pt = &sc->sc_pt[i];
+		pt->pt_sc = sc;
+		pt->pt_nchan = i;
+		pt->pt_proctgt = -1;
+
+		/* Check if channel has any devices besides disks */
+		for (target = 0, lastarget = -1; target < IPS_MAXTARGETS;
+		    target++) {
+			struct ips_dev *idev;
+			int dev_type;
+
+			idev = &sc->sc_info->conf.dev[i][target];
+			dev_type = idev->params & SID_TYPE;
+			if (idev->state && dev_type != T_DIRECT) {
+				lastarget = target;
+				if (type == T_PROCESSOR ||
+				    type == T_ENCLOSURE)
+					/* remember enclosure address */
+					pt->pt_proctgt = target;
+			}
+		}
+		if (lastarget == -1)
+			continue;
+
+		chan = &pt->pt_chan;
+		memset(chan, 0, sizeof(*chan));
+		chan->chan_adapter = adapt;
+		chan->chan_bustype = &scsi_bustype;
+		chan->chan_channel = i;
+		chan->chan_ntargets = IPS_MAXTARGETS;
+		chan->chan_nluns = lastarget + 1;
+		chan->chan_id = i;
+		chan->chan_flags = SCSIPI_CHAN_NOSETTLE;
+		config_found(self, chan, scsiprint);
+	}
+
+	/* Enable interrupts */
+	ips_intren(sc);
+
+#if NBIO > 0
+	/* Install ioctl handler */
+	if (bio_register(&sc->sc_dev, ips_ioctl))
+		printf("%s: no ioctl support\n", sc->sc_dev.dv_xname);
+#endif
+
+	return;
+fail5:
+	ips_ccb_free(sc, sc->sc_ccb, sc->sc_nccbs);
+fail4:
+	if (sc->sc_chip->ic_id == IPS_CHIP_COPPERHEAD)
+		ips_dmamem_free(&sc->sc_sqm);
+fail3:
+	ips_dmamem_free(&sc->sc_infom);
+fail2:
+	ips_dmamem_free(&sc->sc_cmdbm);
+fail1:
+	bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
+}
+
+void
+ips_scsi_cmd(struct ips_ccb *ccb)
+{
+	struct scsipi_xfer *xs = ccb->c_xfer;
+	struct scsipi_periph *periph = xs->xs_periph;
+	struct scsipi_channel *chan = periph->periph_channel;
+	struct ips_softc *sc = device_private(chan->chan_adapter->adapt_dev);
+	struct ips_driveinfo *di = &sc->sc_info->drive;
+	struct ips_drive *drive;
+	struct ips_cmd *cmd;
+	int target = periph->periph_target;
+	u_int32_t blkno, blkcnt;
+	int code;
+
+	DPRINTF(IPS_D_XFER, ("%s: ips_scsi_cmd: xs %p, target %d, "
+	    "opcode 0x%02x, flags 0x%x\n", sc->sc_dev.dv_xname, xs, target,
+	    xs->cmd->opcode, xs->xs_control));
+
+	if (target >= sc->sc_nunits || periph->periph_lun != 0) {
+		DPRINTF(IPS_D_INFO, ("%s: ips_scsi_cmd: invalid params "
+		    "target %d, lun %d\n", sc->sc_dev.dv_xname,
+		    target, periph->periph_lun));
+		xs->error = XS_DRIVER_STUFFUP;
+		ips_ccb_put(sc, ccb);
+		scsipi_done(xs);
+		return;
+	}
+
+	drive = &di->drive[target];
+	xs->error = XS_NOERROR;
+
+	/* Fake SCSI commands */
+	switch (xs->cmd->opcode) {
+	case READ_10:
+	case SCSI_READ_6_COMMAND:
+	case WRITE_10:
+	case SCSI_WRITE_6_COMMAND: {
+		struct scsi_rw_6 *rw;
+		struct scsipi_rw_10 *rwb;
+
+		if (xs->cmdlen == sizeof(struct scsi_rw_6)) {
+			rw = (void *)xs->cmd;
+			blkno = _3btol(rw->addr) &
+			    (SRW_TOPADDR << 16 | 0xffff);
+			blkcnt = rw->length ? rw->length : 0x100;
+		} else {
+			rwb = (void *)xs->cmd;
+			blkno = _4btol(rwb->addr);
+			blkcnt = _2btol(rwb->length);
+		}
+
+		if (blkno >= htole32(drive->seccnt) || blkno + blkcnt >
+		    htole32(drive->seccnt)) {
+			DPRINTF(IPS_D_ERR, ("%s: ips_scsi_cmd: invalid params "
+			    "blkno %u, blkcnt %u\n", sc->sc_dev.dv_xname,
+			    blkno, blkcnt));
+			xs->error = XS_DRIVER_STUFFUP;
+			break;
+		}
+
+		if (xs->xs_control & XS_CTL_DATA_IN)
+			code = IPS_CMD_READ;
+		else
+			code = IPS_CMD_WRITE;
+
+		cmd = ccb->c_cmdbva;
+		cmd->code = code;
+		cmd->drive = target;
+		cmd->lba = htole32(blkno);
+		cmd->seccnt = htole16(blkcnt);
+
+		if (ips_load_xs(sc, ccb, xs)) {
+			DPRINTF(IPS_D_ERR, ("%s: ips_scsi_cmd: ips_load_xs "
+			    "failed\n", sc->sc_dev.dv_xname));
+			xs->error = XS_DRIVER_STUFFUP;
+			ips_ccb_put(sc, ccb);
+			scsipi_done(xs);
+			return;
+		}
+
+		if (cmd->sgcnt > 0)
+			cmd->code |= IPS_CMD_SG;
+
+		ccb->c_done = ips_done_xs;
+		ips_start_xs(sc, ccb, xs);
+		return;
+	}
+	case INQUIRY: {
+		struct scsipi_inquiry_data inq;
+
+		bzero(&inq, sizeof(inq));
+		inq.device = T_DIRECT;
+		inq.version = 2;
+		inq.response_format = 2;
+		inq.additional_length = 32;
+		inq.flags3 |= SID_CmdQue;
+		strlcpy(inq.vendor, "IBM", sizeof(inq.vendor));
+		snprintf(inq.product, sizeof(inq.product),
+		    "LD%d RAID%d", target, drive->raid);
+		strlcpy(inq.revision, "1.0", sizeof(inq.revision));
+		memcpy(xs->data, &inq, MIN(xs->datalen, sizeof(inq)));
+		break;
+	}
+	case READ_CAPACITY_10: {
+		struct scsipi_read_capacity_10_data rcd;
+
+		bzero(&rcd, sizeof(rcd));
+		_lto4b(htole32(drive->seccnt) - 1, rcd.addr);
+		_lto4b(IPS_SECSZ, rcd.length);
+		memcpy(xs->data, &rcd, MIN(xs->datalen, sizeof(rcd)));
+		break;
+	}
+	case SCSI_REQUEST_SENSE: {
+		struct scsi_sense_data sd;
+
+		bzero(&sd, sizeof(sd));
+		sd.response_code = SSD_RCODE_CURRENT;
+		sd.flags = SKEY_NO_SENSE;
+		memcpy(xs->data, &sd, MIN(xs->datalen, sizeof(sd)));
+		break;
+	}
+	case SCSI_SYNCHRONIZE_CACHE_10:
+		cmd = ccb->c_cmdbva;
+		cmd->code = IPS_CMD_FLUSH;
+
+		ccb->c_done = ips_done_xs;
+		ips_start_xs(sc, ccb, xs);
+		return;
+	case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
+	case START_STOP:
+	case SCSI_TEST_UNIT_READY:
+		break;
+	default:
+		DPRINTF(IPS_D_INFO, ("%s: unsupported scsi command 0x%02x\n",
+		    sc->sc_dev.dv_xname, xs->cmd->opcode));
+		xs->error = XS_DRIVER_STUFFUP;
+	}
+
+	ips_ccb_put(sc, ccb);
+	scsipi_done(xs);
+}
+
+/*
+ * Start a SCSI command.
+ */
+static void
+ips_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req,
+		   void *arg)
+{
+	switch (req) {
+	case ADAPTER_REQ_RUN_XFER: {
+		struct ips_ccb *ccb;
+		struct scsipi_xfer *xs;
+		struct ips_softc *sc;
+
+		sc = device_private(chan->chan_adapter->adapt_dev);
+		xs = (struct scsipi_xfer *)arg;
+
+		if ((ccb = ips_ccb_get(sc)) == NULL) {
+			xs->error = XS_RESOURCE_SHORTAGE;
+			scsipi_done(xs);
+			break;
+		}
+
+		ccb->c_xfer = xs;
+		ips_scsi_cmd(ccb);
+
+		break;
+	}
+
+	case ADAPTER_REQ_SET_XFER_MODE: {
+		struct scsipi_xfer_mode *xm = arg;
+		xm->xm_mode = PERIPH_CAP_TQING;
+		xm->xm_period = 0;
+		xm->xm_offset = 0;
+		scsipi_async_event(chan, ASYNC_EVENT_XFER_MODE, xm);
+		return;
+	}
+
+	case ADAPTER_REQ_GROW_RESOURCES:
+		/*
+		 * Not supported.
+		 */
+		break;
+	}
+}
+
+int
+ips_scsi_ioctl(struct scsipi_channel *chan, u_long cmd, void *data,
+    int flag, struct proc *p)
+{
+#if NBIO > 0
+	return (ips_ioctl(chan->chan_adapter->adapt_dev, cmd, data));
+#else
+	return (ENOTTY);
+#endif
+}
+
+#if NBIO > 0
+int
+ips_ioctl(device_t dev, u_long cmd, void *data)
+{
+	struct ips_softc *sc = (struct ips_softc *)dev;
+
+	DPRINTF(IPS_D_INFO, ("%s: ips_ioctl: cmd %lu\n",
+	    sc->sc_dev.dv_xname, cmd));
+
+	switch (cmd) {
+	case BIOCINQ:
+		return (ips_ioctl_inq(sc, (struct bioc_inq *)data));
+	case BIOCVOL:
+		return (ips_ioctl_vol(sc, (struct bioc_vol *)data));
+	case BIOCDISK:
+		return (ips_ioctl_disk(sc, (struct bioc_disk *)data));
+	case BIOCSETSTATE:
+		return (ips_ioctl_setstate(sc, (struct bioc_setstate *)data));
+	default:
+		return (ENOTTY);
+	}
+}
+
+int
+ips_ioctl_inq(struct ips_softc *sc, struct bioc_inq *bi)
+{
+	struct ips_conf *conf = &sc->sc_info->conf;
+	int i;
+
+	strlcpy(bi->bi_dev, sc->sc_dev.dv_xname, sizeof(bi->bi_dev));
+	bi->bi_novol = sc->sc_nunits;
+	for (i = 0, bi->bi_nodisk = 0; i < sc->sc_nunits; i++)
+		bi->bi_nodisk += conf->ld[i].chunkcnt;
+
+	DPRINTF(IPS_D_INFO, ("%s: ips_ioctl_inq: novol %d, nodisk %d\n",
+	    bi->bi_dev, bi->bi_novol, bi->bi_nodisk));
+
+	return (0);
+}
+
+int
+ips_ioctl_vol(struct ips_softc *sc, struct bioc_vol *bv)
+{
+	struct ips_driveinfo *di = &sc->sc_info->drive;
+	struct ips_conf *conf = &sc->sc_info->conf;
+	struct ips_rblstat *rblstat = &sc->sc_info->rblstat;
+	struct ips_ld *ld;
+	int vid = bv->bv_volid;
+	struct device *dv;
+	int error, rebuild = 0;
+	u_int32_t total = 0, done = 0;
+
+	if (vid >= sc->sc_nunits)
+		return (EINVAL);
+	if ((error = ips_getconf(sc, 0)))
+		return (error);
+	ld = &conf->ld[vid];
+
+	switch (ld->state) {
+	case IPS_DS_ONLINE:
+		bv->bv_status = BIOC_SVONLINE;
+		break;
+	case IPS_DS_DEGRADED:
+		bv->bv_status = BIOC_SVDEGRADED;
+		rebuild++;
+		break;
+	case IPS_DS_OFFLINE:
+		bv->bv_status = BIOC_SVOFFLINE;
+		break;
+	default:
+		bv->bv_status = BIOC_SVINVALID;
+	}
+
+	if (rebuild && ips_getrblstat(sc, 0) == 0) {
+		total = htole32(rblstat->ld[vid].total);
+		done = total - htole32(rblstat->ld[vid].remain);
+		if (total && total > done) {
+			bv->bv_status = BIOC_SVREBUILD;
+			bv->bv_percent = 100 * done / total;
+		}
+	}
+
+	bv->bv_size = (uint64_t)htole32(ld->size) * IPS_SECSZ;
+	bv->bv_level = di->drive[vid].raid;
+	bv->bv_nodisk = ld->chunkcnt;
+
+	/* Associate all unused and spare drives with first volume */
+	if (vid == 0) {
+		struct ips_dev *dev;
+		int chan, target;
+
+		for (chan = 0; chan < IPS_MAXCHANS; chan++)
+			for (target = 0; target < IPS_MAXTARGETS; target++) {
+				dev = &conf->dev[chan][target];
+				if (dev->state && !(dev->state &
+				    IPS_DVS_MEMBER) &&
+				    (dev->params & SID_TYPE) == T_DIRECT)
+					bv->bv_nodisk++;
+			}
+	}
+
+	dv = &sc->sc_dev;
+	strlcpy(bv->bv_dev, dv->dv_xname, sizeof(bv->bv_dev));
+	strlcpy(bv->bv_vendor, "IBM", sizeof(bv->bv_vendor));
+
+	DPRINTF(IPS_D_INFO, ("%s: ips_ioctl_vol: vid %d, state 0x%02x, "
+	    "total %u, done %u, size %llu, level %d, nodisk %d, dev %s\n",
+	    sc->sc_dev.dv_xname, vid, ld->state, total, done, bv->bv_size,
+	    bv->bv_level, bv->bv_nodisk, bv->bv_dev));
+
+	return (0);
+}
+
+int
+ips_ioctl_disk(struct ips_softc *sc, struct bioc_disk *bd)
+{
+	struct ips_conf *conf = &sc->sc_info->conf;
+	struct ips_ld *ld;
+	struct ips_chunk *chunk;
+	struct ips_dev *dev;
+	int vid = bd->bd_volid, did = bd->bd_diskid;
+	int chan, target, error, i;
+
+	if (vid >= sc->sc_nunits)
+		return (EINVAL);
+	if ((error = ips_getconf(sc, 0)))
+		return (error);
+	ld = &conf->ld[vid];
+
+	if (did >= ld->chunkcnt) {
+		/* Probably unused or spare drives */
+		if (vid != 0)
+			return (EINVAL);
+
+		i = ld->chunkcnt;
+		for (chan = 0; chan < IPS_MAXCHANS; chan++)
+			for (target = 0; target < IPS_MAXTARGETS; target++) {
+				dev = &conf->dev[chan][target];
+				if (dev->state && !(dev->state &
+				    IPS_DVS_MEMBER) &&
+				    (dev->params & SID_TYPE) == T_DIRECT)
+					if (i++ == did)
+						goto out;
+			}
+	} else {
+		chunk = &ld->chunk[did];
+		chan = chunk->channel;
+		target = chunk->target;
+	}
+
+out:
+	if (chan >= IPS_MAXCHANS || target >= IPS_MAXTARGETS)
+		return (EINVAL);
+	dev = &conf->dev[chan][target];
+
+	bd->bd_channel = chan;
+	bd->bd_target = target;
+	bd->bd_lun = 0;
+	bd->bd_size = (uint64_t)htole32(dev->seccnt) * IPS_SECSZ;
+
+	bzero(bd->bd_vendor, sizeof(bd->bd_vendor));
+	memcpy(bd->bd_vendor, dev->devid, MIN(sizeof(bd->bd_vendor),
+	    sizeof(dev->devid)));
+	strlcpy(bd->bd_procdev, sc->sc_pt[chan].pt_procdev,
+	    sizeof(bd->bd_procdev));
+
+	if (dev->state & IPS_DVS_READY) {
+		bd->bd_status = BIOC_SDUNUSED;
+		if (dev->state & IPS_DVS_MEMBER)
+			bd->bd_status = BIOC_SDONLINE;
+		if (dev->state & IPS_DVS_SPARE)
+			bd->bd_status = BIOC_SDHOTSPARE;
+		if (dev->state & IPS_DVS_REBUILD)
+			bd->bd_status = BIOC_SDREBUILD;
+	} else {
+		bd->bd_status = BIOC_SDOFFLINE;
+	}
+
+	DPRINTF(IPS_D_INFO, ("%s: ips_ioctl_disk: vid %d, did %d, channel %d, "
+	    "target %d, size %llu, state 0x%02x\n", sc->sc_dev.dv_xname,
+	    vid, did, bd->bd_channel, bd->bd_target, bd->bd_size, dev->state));
+
+	return (0);
+}
+
+int
+ips_ioctl_setstate(struct ips_softc *sc, struct bioc_setstate *bs)
+{
+	struct ips_conf *conf = &sc->sc_info->conf;
+	struct ips_dev *dev;
+	int state, error;
+
+	if (bs->bs_channel >= IPS_MAXCHANS || bs->bs_target >= IPS_MAXTARGETS)
+		return (EINVAL);
+	if ((error = ips_getconf(sc, 0)))
+		return (error);
+	dev = &conf->dev[bs->bs_channel][bs->bs_target];
+	state = dev->state;
+
+	switch (bs->bs_status) {
+	case BIOC_SSONLINE:
+		state |= IPS_DVS_READY;
+		break;
+	case BIOC_SSOFFLINE:
+		state &= ~IPS_DVS_READY;
+		break;
+	case BIOC_SSHOTSPARE:
+		state |= IPS_DVS_SPARE;
+		break;
+	case BIOC_SSREBUILD:
+		return (ips_rebuild(sc, bs->bs_channel, bs->bs_target,
+		    bs->bs_channel, bs->bs_target, 0));
+	default:
+		return (EINVAL);
+	}
+
+	return (ips_setstate(sc, bs->bs_channel, bs->bs_target, state, 0));
+}
+#endif	/* NBIO > 0 */
+
+int
+ips_load_xs(struct ips_softc *sc, struct ips_ccb *ccb, struct scsipi_xfer *xs)
+{
+	struct ips_cmdb *cmdb = ccb->c_cmdbva;
+	struct ips_cmd *cmd = &cmdb->cmd;
+	struct ips_sg *sg = cmdb->sg;
+	int nsegs, i;
+
+	if (xs->datalen == 0)
+		return (0);
+
+	/* Map data buffer into DMA segments */
+	if (bus_dmamap_load(sc->sc_dmat, ccb->c_dmam, xs->data, xs->datalen,
+	    NULL, (xs->xs_control & XS_CTL_NOSLEEP ? BUS_DMA_NOWAIT : 0)))
+		return (1);
+	bus_dmamap_sync(sc->sc_dmat, ccb->c_dmam, 0,ccb->c_dmam->dm_mapsize,
+	    xs->xs_control & XS_CTL_DATA_IN ? BUS_DMASYNC_PREREAD :
+	    BUS_DMASYNC_PREWRITE);
+
+	if ((nsegs = ccb->c_dmam->dm_nsegs) > IPS_MAXSGS)
+		return (1);
+
+	if (nsegs > 1) {
+		cmd->sgcnt = nsegs;
+		cmd->sgaddr = htole32(ccb->c_cmdbpa + offsetof(struct ips_cmdb,
+		    sg));
+
+		/* Fill in scatter-gather array */
+		for (i = 0; i < nsegs; i++) {
+			sg[i].addr = htole32(ccb->c_dmam->dm_segs[i].ds_addr);
+			sg[i].size = htole32(ccb->c_dmam->dm_segs[i].ds_len);
+		}
+	} else {
+		cmd->sgcnt = 0;
+		cmd->sgaddr = htole32(ccb->c_dmam->dm_segs[0].ds_addr);
+	}
+
+	return (0);
+}
+
+void
+ips_start_xs(struct ips_softc *sc, struct ips_ccb *ccb, struct scsipi_xfer *xs)
+{
+	ccb->c_flags = xs->xs_control;
+	ccb->c_xfer = xs;
+	int ispoll = xs->xs_control & XS_CTL_POLL;
+
+	if (!ispoll) {
+		int timeout = mstohz(xs->timeout);
+		if (timeout == 0)
+			timeout = 1;
+
+		callout_reset(&xs->xs_callout, timeout, ips_timeout, ccb);
+	}
+
+	/*
+	 * Return value not used here because ips_cmd() must complete
+	 * scsipi_xfer on any failure and SCSI layer will handle possible
+	 * errors.
+	 */
+	ips_cmd(sc, ccb);
+}
+
+int
+ips_cmd(struct ips_softc *sc, struct ips_ccb *ccb)
+{
+	struct ips_cmd *cmd = ccb->c_cmdbva;
+	int s, error = 0;
+
+	DPRINTF(IPS_D_XFER, ("%s: ips_cmd: id 0x%02x, flags 0x%x, xs %p, "
+	    "code 0x%02x, drive %d, sgcnt %d, lba %d, sgaddr 0x%08x, "
+	    "seccnt %d\n", sc->sc_dev.dv_xname, ccb->c_id, ccb->c_flags,
+	    ccb->c_xfer, cmd->code, cmd->drive, cmd->sgcnt, htole32(cmd->lba),
+	    htole32(cmd->sgaddr), htole16(cmd->seccnt)));
+
+	cmd->id = ccb->c_id;
+
+	/* Post command to controller and optionally wait for completion */
+	s = splbio();
+	ips_exec(sc, ccb);
+	ccb->c_state = IPS_CCB_QUEUED;
+	if (ccb->c_flags & XS_CTL_POLL)
+		error = ips_poll(sc, ccb);
+	splx(s);
+
+	return (error);
+}
+
+int
+ips_poll(struct ips_softc *sc, struct ips_ccb *ccb)
+{
+	struct timeval tv;
+	int error, timo;
+
+	if (ccb->c_flags & XS_CTL_NOSLEEP) {
+		/* busy-wait */
+		DPRINTF(IPS_D_XFER, ("%s: ips_poll: busy-wait\n",
+		    sc->sc_dev.dv_xname));
+
+		for (timo = 10000; timo > 0; timo--) {
+			delay(100);
+			ips_intr(sc);
+			if (ccb->c_state == IPS_CCB_DONE)
+				break;
+		}
+	} else {
+		/* sleep */
+		timo = ccb->c_xfer ? ccb->c_xfer->timeout : IPS_TIMEOUT;
+		tv.tv_sec = timo / 1000;
+		tv.tv_usec = (timo % 1000) * 1000;
+		timo = tvtohz(&tv);
+
+		DPRINTF(IPS_D_XFER, ("%s: ips_poll: sleep %d hz\n",
+		    sc->sc_dev.dv_xname, timo));
+		tsleep(ccb, PRIBIO + 1, "ipscmd", timo);
+	}
+	DPRINTF(IPS_D_XFER, ("%s: ips_poll: state %d\n", sc->sc_dev.dv_xname,
+	    ccb->c_state));
+
+	if (ccb->c_state != IPS_CCB_DONE)
+		/*
+		 * Command never completed. Fake hardware status byte
+		 * to indicate timeout.
+		 */
+		ccb->c_stat = IPS_STAT_TIMO;
+
+	ips_done(sc, ccb);
+	error = ccb->c_error;
+
+	return (error);
+}
+
+void
+ips_done(struct ips_softc *sc, struct ips_ccb *ccb)
+{
+	DPRINTF(IPS_D_XFER, ("%s: ips_done: id 0x%02x, flags 0x%x, xs %p\n",
+	    sc->sc_dev.dv_xname, ccb->c_id, ccb->c_flags, ccb->c_xfer));
+
+	ccb->c_error = ips_error(sc, ccb);
+	ccb->c_done(sc, ccb);
+}
+
+void
+ips_done_xs(struct ips_softc *sc, struct ips_ccb *ccb)
+{
+	struct scsipi_xfer *xs = ccb->c_xfer;
+
+	if (!(xs->xs_control & XS_CTL_POLL))
+		callout_stop(&xs->xs_callout);
+
+	if (xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT)) {
+		bus_dmamap_sync(sc->sc_dmat, ccb->c_dmam, 0,
+		    ccb->c_dmam->dm_mapsize, xs->xs_control & XS_CTL_DATA_IN ?
+		    BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
+		bus_dmamap_unload(sc->sc_dmat, ccb->c_dmam);
+	}
+
+	xs->resid = 0;
+	xs->error = ips_error_xs(sc, ccb);
+	ips_ccb_put(sc, ccb);
+	scsipi_done(xs);
+}
+
+void
+ips_done_pt(struct ips_softc *sc, struct ips_ccb *ccb)
+{
+	struct scsipi_xfer *xs = ccb->c_xfer;
+	struct ips_cmdb *cmdb = ccb->c_cmdbva;
+	struct ips_dcdb *dcdb = &cmdb->dcdb;
+	int done = htole16(dcdb->datalen);
+
+	if (!(xs->xs_control & XS_CTL_POLL))
+		callout_stop(&xs->xs_callout);
+
+	if (xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT)) {
+		bus_dmamap_sync(sc->sc_dmat, ccb->c_dmam, 0,
+		    ccb->c_dmam->dm_mapsize, xs->xs_control & XS_CTL_DATA_IN ?
+		    BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
+		bus_dmamap_unload(sc->sc_dmat, ccb->c_dmam);
+	}
+
+	if (done && done < xs->datalen)
+		xs->resid = xs->datalen - done;
+	else
+		xs->resid = 0;
+	xs->error = ips_error_xs(sc, ccb);
+	xs->status = dcdb->status;
+
+	if (xs->error == XS_SENSE)
+		memcpy(&xs->sense, dcdb->sense, MIN(sizeof(xs->sense),
+		    sizeof(dcdb->sense)));
+
+	if (xs->cmd->opcode == INQUIRY && xs->error == XS_NOERROR) {
+		int type = ((struct scsipi_inquiry_data *)xs->data)->device &
+		    SID_TYPE;
+
+		if (type == T_DIRECT)
+			/* mask physical drives */
+			xs->error = XS_DRIVER_STUFFUP;
+	}
+
+	ips_ccb_put(sc, ccb);
+	scsipi_done(xs);
+}
+
+void
+ips_done_mgmt(struct ips_softc *sc, struct ips_ccb *ccb)
+{
+	if (ccb->c_flags & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT))
+		bus_dmamap_sync(sc->sc_dmat, sc->sc_infom.dm_map, 0,
+		    sc->sc_infom.dm_map->dm_mapsize,
+		    ccb->c_flags & XS_CTL_DATA_IN ? BUS_DMASYNC_POSTREAD :
+		    BUS_DMASYNC_POSTWRITE);
+
+	ips_ccb_put(sc, ccb);
+}
+
+int
+ips_error(struct ips_softc *sc, struct ips_ccb *ccb)
+{
+	struct ips_cmdb *cmdb = ccb->c_cmdbva;
+	struct ips_cmd *cmd = &cmdb->cmd;
+	struct ips_dcdb *dcdb = &cmdb->dcdb;
+	struct scsipi_xfer *xs = ccb->c_xfer;
+	u_int8_t gsc = IPS_STAT_GSC(ccb->c_stat);
+
+	if (gsc == IPS_STAT_OK)
+		return (0);
+
+	DPRINTF(IPS_D_ERR, ("%s: ips_error: stat 0x%02x, estat 0x%02x, "
+	    "cmd code 0x%02x, drive %d, sgcnt %d, lba %u, seccnt %d",
+	    sc->sc_dev.dv_xname, ccb->c_stat, ccb->c_estat, cmd->code,
+	    cmd->drive, cmd->sgcnt, htole32(cmd->lba), htole16(cmd->seccnt)));
+	if (cmd->code == IPS_CMD_DCDB || cmd->code == IPS_CMD_DCDB_SG) {
+		int i;
+
+		DPRINTF(IPS_D_ERR, (", dcdb device 0x%02x, attr 0x%02x, "
+		    "datalen %d, sgcnt %d, status 0x%02x",
+		    dcdb->device, dcdb->attr, htole16(dcdb->datalen),
+		    dcdb->sgcnt, dcdb->status));
+
+		DPRINTF(IPS_D_ERR, (", cdb"));
+		for (i = 0; i < dcdb->cdblen; i++)
+			DPRINTF(IPS_D_ERR, (" %x", dcdb->cdb[i]));
+		if (ccb->c_estat == IPS_ESTAT_CKCOND) {
+			DPRINTF(IPS_D_ERR, (", sense"));
+			for (i = 0; i < dcdb->senselen; i++)
+				DPRINTF(IPS_D_ERR, (" %x", dcdb->sense[i]));
+		}
+	}		
+	DPRINTF(IPS_D_ERR, ("\n"));
+
+	switch (gsc) {
+	case IPS_STAT_RECOV:
+		return (0);
+	case IPS_STAT_INVOP:
+	case IPS_STAT_INVCMD:
+	case IPS_STAT_INVPARM:
+		return (EINVAL);
+	case IPS_STAT_BUSY:
+		return (EBUSY);
+	case IPS_STAT_TIMO:
+		return (ETIMEDOUT);
+	case IPS_STAT_PDRVERR:
+		switch (ccb->c_estat) {
+		case IPS_ESTAT_SELTIMO:
+			return (ENODEV);
+		case IPS_ESTAT_OURUN:
+			if (xs && htole16(dcdb->datalen) < xs->datalen)
+				/* underrun */
+				return (0);
+			break;
+		case IPS_ESTAT_RECOV:
+			return (0);
+		}
+		break;
+	}
+
+	return (EIO);
+}
+
+int
+ips_error_xs(struct ips_softc *sc, struct ips_ccb *ccb)
+{
+	struct ips_cmdb *cmdb = ccb->c_cmdbva;
+	struct ips_dcdb *dcdb = &cmdb->dcdb;
+	struct scsipi_xfer *xs = ccb->c_xfer;
+	u_int8_t gsc = IPS_STAT_GSC(ccb->c_stat);
+
+	/* Map hardware error codes to SCSI ones */
+	switch (gsc) {
+	case IPS_STAT_OK:
+	case IPS_STAT_RECOV:
+		return (XS_NOERROR);
+	case IPS_STAT_BUSY:
+		return (XS_BUSY);
+	case IPS_STAT_TIMO:
+		return (XS_TIMEOUT);
+	case IPS_STAT_PDRVERR:
+		switch (ccb->c_estat) {
+		case IPS_ESTAT_SELTIMO:
+			return (XS_SELTIMEOUT);
+		case IPS_ESTAT_OURUN:
+			if (xs && htole16(dcdb->datalen) < xs->datalen)
+				/* underrun */
+				return (XS_NOERROR);
+			break;
+		case IPS_ESTAT_HOSTRST:
+		case IPS_ESTAT_DEVRST:
+			return (XS_RESET);
+		case IPS_ESTAT_RECOV:
+			return (XS_NOERROR);
+		case IPS_ESTAT_CKCOND:
+			return (XS_SENSE);
+		}
+		break;
+	}
+
+	return (XS_DRIVER_STUFFUP);
+}
+
+int
+ips_intr(void *arg)
+{
+	struct ips_softc *sc = arg;
+	struct ips_ccb *ccb;
+	u_int32_t status;
+	int id;
+
+	DPRINTF(IPS_D_XFER, ("%s: ips_intr", sc->sc_dev.dv_xname));
+	if (!ips_isintr(sc)) {
+		DPRINTF(IPS_D_XFER, (": not ours\n"));
+		return (0);
+	}
+	DPRINTF(IPS_D_XFER, ("\n"));
+
+	/* Process completed commands */
+	while ((status = ips_status(sc)) != 0xffffffff) {
+		DPRINTF(IPS_D_XFER, ("%s: ips_intr: status 0x%08x\n",
+		    sc->sc_dev.dv_xname, status));
+
+		id = IPS_STAT_ID(status);
+		if (id >= sc->sc_nccbs) {
+			DPRINTF(IPS_D_ERR, ("%s: ips_intr: invalid id %d\n",
+			    sc->sc_dev.dv_xname, id));
+			continue;
+		}
+
+		ccb = &sc->sc_ccb[id];
+		if (ccb->c_state != IPS_CCB_QUEUED) {
+			DPRINTF(IPS_D_ERR, ("%s: ips_intr: cmd 0x%02x not "
+			    "queued, state %d, status 0x%08x\n",
+			    sc->sc_dev.dv_xname, ccb->c_id, ccb->c_state,
+			    status));
+			continue;
+		}
+
+		ccb->c_state = IPS_CCB_DONE;
+		ccb->c_stat = IPS_STAT_BASIC(status);
+		ccb->c_estat = IPS_STAT_EXT(status);
+
+		if (ccb->c_flags & XS_CTL_POLL) {
+			wakeup(ccb);
+		} else {
+			ips_done(sc, ccb);
+		}
+	}
+
+	return (1);
+}
+
+void
+ips_timeout(void *arg)
+{
+	struct ips_ccb *ccb = arg;
+	struct ips_softc *sc = ccb->c_sc;
+	struct scsipi_xfer *xs = ccb->c_xfer;
+	int s;
+
+	s = splbio();
+	if (xs)
+		scsi_print_addr(xs->xs_periph);
+	else
+		printf("%s: ", sc->sc_dev.dv_xname);
+	printf("timeout\n");
+
+	/*
+	 * Command never completed. Fake hardware status byte
+	 * to indicate timeout.
+	 * XXX: need to remove command from controller.
+	 */
+	ccb->c_stat = IPS_STAT_TIMO;
+	ips_done(sc, ccb);
+	splx(s);
+}
+
+int
+ips_getadapterinfo(struct ips_softc *sc, int flags)
+{
+	struct ips_ccb *ccb;
+	struct ips_cmd *cmd;
+
+	ccb = ips_ccb_get(sc);
+	if (ccb == NULL)
+		return (1);
+
+	ccb->c_flags = XS_CTL_DATA_IN | XS_CTL_POLL | flags;
+	ccb->c_done = ips_done_mgmt;
+
+	cmd = ccb->c_cmdbva;
+	cmd->code = IPS_CMD_GETADAPTERINFO;
+	cmd->sgaddr = htole32(sc->sc_infom.dm_paddr + offsetof(struct ips_info,
+	    adapter));
+
+	return (ips_cmd(sc, ccb));
+}
+
+int
+ips_getdriveinfo(struct ips_softc *sc, int flags)
+{
+	struct ips_ccb *ccb;
+	struct ips_cmd *cmd;
+
+	ccb = ips_ccb_get(sc);
+	if (ccb == NULL)
+		return (1);
+
+	ccb->c_flags = XS_CTL_DATA_IN | XS_CTL_POLL | flags;
+	ccb->c_done = ips_done_mgmt;
+
+	cmd = ccb->c_cmdbva;
+	cmd->code = IPS_CMD_GETDRIVEINFO;
+	cmd->sgaddr = htole32(sc->sc_infom.dm_paddr + offsetof(struct ips_info,
+	    drive));
+
+	return (ips_cmd(sc, ccb));
+}
+
+int
+ips_getconf(struct ips_softc *sc, int flags)
+{
+	struct ips_ccb *ccb;
+	struct ips_cmd *cmd;
+
+	ccb = ips_ccb_get(sc);
+	if (ccb == NULL)
+		return (1);
+
+	ccb->c_flags = XS_CTL_DATA_IN | XS_CTL_POLL | flags;
+	ccb->c_done = ips_done_mgmt;
+
+	cmd = ccb->c_cmdbva;
+	cmd->code = IPS_CMD_READCONF;
+	cmd->sgaddr = htole32(sc->sc_infom.dm_paddr + offsetof(struct ips_info,
+	    conf));
+
+	return (ips_cmd(sc, ccb));
+}
+
+int
+ips_getpg5(struct ips_softc *sc, int flags)
+{
+	struct ips_ccb *ccb;
+	struct ips_cmd *cmd;
+
+	ccb = ips_ccb_get(sc);
+	if (ccb == NULL)
+		return (1);
+
+	ccb->c_flags = XS_CTL_DATA_IN | XS_CTL_POLL | flags;
+	ccb->c_done = ips_done_mgmt;
+
+	cmd = ccb->c_cmdbva;
+	cmd->code = IPS_CMD_RWNVRAM;
+	cmd->drive = 5;
+	cmd->sgaddr = htole32(sc->sc_infom.dm_paddr + offsetof(struct ips_info,
+	    pg5));
+
+	return (ips_cmd(sc, ccb));
+}
+
+#if NBIO > 0
+int
+ips_getrblstat(struct ips_softc *sc, int flags)
+{
+	struct ips_ccb *ccb;
+	struct ips_cmd *cmd;
+
+	ccb = ips_ccb_get(sc);
+	if (ccb == NULL)
+		return (1);
+
+	ccb->c_flags = XS_CTL_DATA_IN | XS_CTL_POLL | flags;
+	ccb->c_done = ips_done_mgmt;
+
+	cmd = ccb->c_cmdbva;
+	cmd->code = IPS_CMD_REBUILDSTATUS;
+	cmd->sgaddr = htole32(sc->sc_infom.dm_paddr + offsetof(struct ips_info,
+	    rblstat));
+
+	return (ips_cmd(sc, ccb));
+}
+
+int
+ips_setstate(struct ips_softc *sc, int chan, int target, int state, int flags)
+{
+	struct ips_ccb *ccb;
+	struct ips_cmd *cmd;
+
+	ccb = ips_ccb_get(sc);
+	if (ccb == NULL)
+		return (1);
+
+	ccb->c_flags = XS_CTL_POLL | flags;
+	ccb->c_done = ips_done_mgmt;
+
+	cmd = ccb->c_cmdbva;
+	cmd->code = IPS_CMD_SETSTATE;
+	cmd->drive = chan;
+	cmd->sgcnt = target;
+	cmd->seg4g = state;
+
+	return (ips_cmd(sc, ccb));
+}
+
+int
+ips_rebuild(struct ips_softc *sc, int chan, int target, int nchan,
+    int ntarget, int flags)
+{
+	struct ips_ccb *ccb;
+	struct ips_cmd *cmd;
+
+	ccb = ips_ccb_get(sc);
+	if (ccb == NULL)
+		return (1);
+
+	ccb->c_flags = XS_CTL_POLL | flags;
+	ccb->c_done = ips_done_mgmt;
+
+	cmd = ccb->c_cmdbva;
+	cmd->code = IPS_CMD_REBUILD;
+	cmd->drive = chan;
+	cmd->sgcnt = target;
+	cmd->seccnt = htole16(ntarget << 8 | nchan);
+
+	return (ips_cmd(sc, ccb));
+}
+#endif	/* NBIO > 0 */
+
+void
+ips_copperhead_exec(struct ips_softc *sc, struct ips_ccb *ccb)
+{
+	u_int32_t reg;
+	int timeout;
+
+	for (timeout = 100; timeout-- > 0; delay(100)) {
+		reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, IPS_REG_CCC);
+		if ((reg & IPS_REG_CCC_SEM) == 0)
+			break;
+	}
+	if (timeout < 0) {
+		printf("%s: semaphore timeout\n", sc->sc_dev.dv_xname);
+		return;
+	}
+
+	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IPS_REG_CCSA, ccb->c_cmdbpa);
+	bus_space_write_2(sc->sc_iot, sc->sc_ioh, IPS_REG_CCC,
+	    IPS_REG_CCC_START);
+}
+
+void
+ips_copperhead_intren(struct ips_softc *sc)
+{
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh, IPS_REG_HIS, IPS_REG_HIS_EN);
+}
+
+int
+ips_copperhead_isintr(struct ips_softc *sc)
+{
+	u_int8_t reg;
+
+	reg = bus_space_read_1(sc->sc_iot, sc->sc_ioh, IPS_REG_HIS);
+	bus_space_write_1(sc->sc_iot, sc->sc_ioh, IPS_REG_HIS, reg);
+	if (reg != 0xff && (reg & IPS_REG_HIS_SCE))
+		return (1);
+
+	return (0);
+}
+
+u_int32_t
+ips_copperhead_status(struct ips_softc *sc)
+{
+	u_int32_t sqhead, sqtail, status;
+
+	sqhead = bus_space_read_4(sc->sc_iot, sc->sc_ioh, IPS_REG_SQH);
+	DPRINTF(IPS_D_XFER, ("%s: sqhead 0x%08x, sqtail 0x%08x\n",
+	    sc->sc_dev.dv_xname, sqhead, sc->sc_sqtail));
+
+	sqtail = sc->sc_sqtail + sizeof(u_int32_t);
+	if (sqtail == sc->sc_sqm.dm_paddr + IPS_SQSZ)
+		sqtail = sc->sc_sqm.dm_paddr;
+	if (sqtail == sqhead)
+		return (0xffffffff);
+
+	sc->sc_sqtail = sqtail;
+	if (++sc->sc_sqidx == IPS_MAXCMDS)
+		sc->sc_sqidx = 0;
+	status = htole32(sc->sc_sqbuf[sc->sc_sqidx]);
+	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IPS_REG_SQT, sqtail);
+
+	return (status);
+}
+
+void
+ips_morpheus_exec(struct ips_softc *sc, struct ips_ccb *ccb)
+{
+	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IPS_REG_IQP, ccb->c_cmdbpa);
+}
+
+void
+ips_morpheus_intren(struct ips_softc *sc)
+{
+	u_int32_t reg;
+
+	reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, IPS_REG_OIM);
+	reg &= ~IPS_REG_OIM_DS;
+	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IPS_REG_OIM, reg);
+}
+
+int
+ips_morpheus_isintr(struct ips_softc *sc)
+{
+	return (bus_space_read_4(sc->sc_iot, sc->sc_ioh, IPS_REG_OIS) &
+	    IPS_REG_OIS_PEND);
+}
+
+u_int32_t
+ips_morpheus_status(struct ips_softc *sc)
+{
+	u_int32_t reg;
+
+	reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, IPS_REG_OQP);
+	DPRINTF(IPS_D_XFER, ("%s: status 0x%08x\n", sc->sc_dev.dv_xname, reg));
+
+	return (reg);
+}
+
+struct ips_ccb *
+ips_ccb_alloc(struct ips_softc *sc, int n)
+{
+	struct ips_ccb *ccb;
+	int i;
+
+	if ((ccb = malloc(n * sizeof(*ccb), M_DEVBUF,
+	    M_NOWAIT | M_ZERO)) == NULL)
+		return (NULL);
+
+	for (i = 0; i < n; i++) {
+		ccb[i].c_sc = sc;
+		ccb[i].c_id = i;
+		ccb[i].c_cmdbva = (char *)sc->sc_cmdbm.dm_vaddr +
+		    i * sizeof(struct ips_cmdb);
+		ccb[i].c_cmdbpa = sc->sc_cmdbm.dm_paddr +
+		    i * sizeof(struct ips_cmdb);
+		if (bus_dmamap_create(sc->sc_dmat, IPS_MAXFER, IPS_MAXSGS,
+		    IPS_MAXFER, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
+		    &ccb[i].c_dmam))
+			goto fail;
+	}
+
+	return (ccb);
+fail:
+	for (; i > 0; i--)
+		bus_dmamap_destroy(sc->sc_dmat, ccb[i - 1].c_dmam);
+	free(ccb, M_DEVBUF);
+	return (NULL);
+}
+
+void
+ips_ccb_free(struct ips_softc *sc, struct ips_ccb *ccb, int n)
+{
+	int i;
+
+	for (i = 0; i < n; i++)
+		bus_dmamap_destroy(sc->sc_dmat, ccb[i - 1].c_dmam);
+	free(ccb, M_DEVBUF);
+}
+
+struct ips_ccb *
+ips_ccb_get(struct ips_softc *sc)
+{
+	struct ips_ccb *ccb;
+
+	mutex_enter(&sc->sc_ccb_mtx);
+	if ((ccb = SLIST_FIRST(&sc->sc_ccbq_free)) != NULL) {
+		SLIST_REMOVE_HEAD(&sc->sc_ccbq_free, c_link);
+		ccb->c_flags = 0;
+		ccb->c_xfer = NULL;
+		bzero(ccb->c_cmdbva, sizeof(struct ips_cmdb));
+	}
+	mutex_exit(&sc->sc_ccb_mtx);
+
+	return (ccb);
+}
+
+void
+ips_ccb_put(struct ips_softc *sc, struct ips_ccb *ccb)
+{
+	ccb->c_state = IPS_CCB_FREE;
+	mutex_enter(&sc->sc_ccb_mtx);
+	SLIST_INSERT_HEAD(&sc->sc_ccbq_free, ccb, c_link);
+	mutex_exit(&sc->sc_ccb_mtx);
+}
+
+int
+ips_dmamem_alloc(struct dmamem *dm, bus_dma_tag_t tag, bus_size_t size)
+{
+	int nsegs;
+
+	dm->dm_tag = tag;
+	dm->dm_size = size;
+
+	if (bus_dmamap_create(tag, size, 1, size, 0,
+	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &dm->dm_map))
+		return (1);
+	if (bus_dmamem_alloc(tag, size, 0, 0, &dm->dm_seg, 1, &nsegs,
+	    BUS_DMA_NOWAIT))
+		goto fail1;
+	if (bus_dmamem_map(tag, &dm->dm_seg, 1, size, &dm->dm_vaddr,
+	    BUS_DMA_NOWAIT))
+		goto fail2;
+	if (bus_dmamap_load(tag, dm->dm_map, dm->dm_vaddr, size, NULL,
+	    BUS_DMA_NOWAIT))
+		goto fail3;
+
+	return (0);
+
+fail3:
+	bus_dmamem_unmap(tag, dm->dm_vaddr, size);
+fail2:
+	bus_dmamem_free(tag, &dm->dm_seg, 1);
+fail1:
+	bus_dmamap_destroy(tag, dm->dm_map);
+	return (1);
+}
+
+void
+ips_dmamem_free(struct dmamem *dm)
+{
+	bus_dmamap_unload(dm->dm_tag, dm->dm_map);
+	bus_dmamem_unmap(dm->dm_tag, dm->dm_vaddr, dm->dm_size);
+	bus_dmamem_free(dm->dm_tag, &dm->dm_seg, 1);
+	bus_dmamap_destroy(dm->dm_tag, dm->dm_map);
+}

Reply via email to