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 <[email protected]>
+ *
+ * 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);
+}