Salut, I got ips to work partially, but I have a Copperhead machine so I am still working on the machine specifica. I am currently devoting most of my work to Copperhead because the rest appears to work at first glance, and in order to really test it I need support for the only ips machine I have. It compiles though and loads and discovers the SCSI bus, even sends commands, the communication appears to work. If someone with a morpheus or related could confirm it is working (or tell me why it's not) that would be really great. Tonnerre
Index: sys/arch/i386/conf/ALL =================================================================== RCS file: /cvsroot/src/sys/arch/i386/conf/ALL,v retrieving revision 1.73.2.9 diff -u -r1.73.2.9 ALL --- sys/arch/i386/conf/ALL 27 Sep 2007 22:04:27 -0000 1.73.2.9 +++ sys/arch/i386/conf/ALL 10 Jan 2008 19:30:28 -0000 @@ -634,6 +634,7 @@ bha* at pci? dev ? function ? # BusLogic 9xx SCSI dpt* at pci? dev ? function ? # DPT SmartCache/SmartRAID iha* at pci? dev ? function ? # Initio INIC-940/950 SCSI +ips* at pci? dev ? function ? # IBM ServeRAID and compatible isp* at pci? dev ? function ? # Qlogic ISP [12]0x0 SCSI/FibreChannel mfi* at pci? dev ? function ? # LSI MegaRAID SAS mly* at pci? dev ? function ? # Mylex AcceleRAID and eXtremeRAID Index: sys/arch/i386/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/i386/conf/GENERIC,v retrieving revision 1.799.2.15 diff -u -r1.799.2.15 GENERIC --- sys/arch/i386/conf/GENERIC 26 Nov 2007 21:35:47 -0000 1.799.2.15 +++ sys/arch/i386/conf/GENERIC 10 Jan 2008 19:30:28 -0000 @@ -641,6 +641,7 @@ bha* at pci? dev ? function ? # BusLogic 9xx SCSI dpt* at pci? dev ? function ? # DPT SmartCache/SmartRAID iha* at pci? dev ? function ? # Initio INIC-940/950 SCSI +ips* at pci? dev ? function ? # IBM ServeRAID and compatible isp* at pci? dev ? function ? # Qlogic ISP [12]0x0 SCSI/FibreChannel mfi* at pci? dev ? function ? # LSI MegaRAID SAS mly* at pci? dev ? function ? # Mylex AcceleRAID and eXtremeRAID Index: sys/arch/i386/conf/INSTALL =================================================================== RCS file: /cvsroot/src/sys/arch/i386/conf/INSTALL,v retrieving revision 1.297.2.5 diff -u -r1.297.2.5 INSTALL --- sys/arch/i386/conf/INSTALL 9 May 2007 22:52:25 -0000 1.297.2.5 +++ sys/arch/i386/conf/INSTALL 10 Jan 2008 19:30:28 -0000 @@ -339,6 +339,7 @@ bha* at pci? dev ? function ? # BusLogic 9xx SCSI dpt* at pci? dev ? function ? # DPT SmartCache/SmartRAID iha* at pci? dev ? function ? # Initio INIC-940/950 SCSI +ips* at pci? dev ? function ? # IBM ServeRAID and compatible isp* at pci? dev ? function ? # Qlogic ISP [12]0x0 SCSI/FibreChannel mfi* at pci? dev ? function ? # LSI MegaRAID SAS mly* at pci? dev ? function ? # Mylex AcceleRAID and eXtremeRAID Index: sys/arch/i386/conf/XEN2_DOM0 =================================================================== RCS file: /cvsroot/src/sys/arch/i386/conf/XEN2_DOM0,v retrieving revision 1.18.2.5 diff -u -r1.18.2.5 XEN2_DOM0 --- sys/arch/i386/conf/XEN2_DOM0 31 Aug 2007 20:09:27 -0000 1.18.2.5 +++ sys/arch/i386/conf/XEN2_DOM0 10 Jan 2008 19:30:28 -0000 @@ -411,6 +411,7 @@ bha* at pci? dev ? function ? # BusLogic 9xx SCSI dpt* at pci? dev ? function ? # DPT SmartCache/SmartRAID iha* at pci? dev ? function ? # Initio INIC-940/950 SCSI +ips* at pci? dev ? function ? # IBM ServeRAID and compatible isp* at pci? dev ? function ? # Qlogic ISP [12]0x0 SCSI/FibreChannel mfi* at pci? dev ? function ? # LSI MegaRAID SAS mly* at pci? dev ? function ? # Mylex AcceleRAID and eXtremeRAID Index: sys/dev/pci/files.pci =================================================================== RCS file: /cvsroot/src/sys/dev/pci/files.pci,v retrieving revision 1.273.2.3 diff -u -r1.273.2.3 files.pci --- sys/dev/pci/files.pci 31 Aug 2007 20:09:25 -0000 1.273.2.3 +++ sys/dev/pci/files.pci 10 Jan 2008 19:30:34 -0000 @@ -142,6 +142,11 @@ attach mpt at pci with mpt_pci file dev/pci/mpt_pci.c mpt_pci +# IBM ServeRAID controllers +device ips: scsi +attach ips at pci +file dev/pci/ips.c ips + # Ethernet driver for DC21040-based boards device de: ether, ifnet, arp attach de at pci Index: sys/dev/pci/pcidevs =================================================================== RCS file: /cvsroot/src/sys/dev/pci/pcidevs,v retrieving revision 1.855.2.16 diff -u -r1.855.2.16 pcidevs --- sys/dev/pci/pcidevs 15 Nov 2007 10:28:24 -0000 1.855.2.16 +++ sys/dev/pci/pcidevs 10 Jan 2008 19:30:34 -0000 @@ -822,6 +822,8 @@ product ADP2 PERC_3QC 0x1365 Dell PERC 3/QC product ADP2 HP_M110_G2 0x3227 HP M110 G2 / ASR-2610SA +product ADP2 SERVERAID 0x0250 ADP2 ServeRAID + /* Addtron Products */ product ADDTRON 8139 0x1360 8139 Ethernet product ADDTRON RHINEII 0x1320 Rhine II 10/100 Ethernet Index: sys/dev/pci/pcidevs.h =================================================================== RCS file: /cvsroot/src/sys/dev/pci/pcidevs.h,v retrieving revision 1.854.2.16 diff -u -r1.854.2.16 pcidevs.h --- sys/dev/pci/pcidevs.h 15 Nov 2007 10:28:25 -0000 1.854.2.16 +++ sys/dev/pci/pcidevs.h 10 Jan 2008 19:30:34 -0000 @@ -829,6 +829,8 @@ #define PCI_PRODUCT_ADP2_PERC_3QC 0x1365 /* Dell PERC 3/QC */ #define PCI_PRODUCT_ADP2_HP_M110_G2 0x3227 /* HP M110 G2 / ASR-2610SA */ +#define PCI_PRODUCT_ADP2_SERVERAID 0x0250 /* ADP2 ServeRAID */ + /* Addtron Products */ #define PCI_PRODUCT_ADDTRON_8139 0x1360 /* 8139 Ethernet */ #define PCI_PRODUCT_ADDTRON_RHINEII 0x1320 /* Rhine II 10/100 Ethernet */ Index: sys/dev/pci/pcidevs_data.h =================================================================== RCS file: /cvsroot/src/sys/dev/pci/pcidevs_data.h,v retrieving revision 1.853.2.16 diff -u -r1.853.2.16 pcidevs_data.h --- sys/dev/pci/pcidevs_data.h 15 Nov 2007 10:28:26 -0000 1.853.2.16 +++ sys/dev/pci/pcidevs_data.h 10 Jan 2008 19:30:35 -0000 @@ -3012,6 +3012,10 @@ "Dell PERC 3/QC", }, { + PCI_VENDOR_ADP2, PCI_PRODUCT_ADP2_SERVERAID, + "ADP2 ServeRAID", + }, + { PCI_VENDOR_ADP2, PCI_PRODUCT_ADP2_HP_M110_G2, "HP M110 G2 / ASR-2610SA", }, @@ -12888,4 +12892,4 @@ "Video Controller", }, }; -const int pci_nproducts = 2627; +const int pci_nproducts = 2628; Index: sys/dev/scsipi/scsipi_all.h =================================================================== RCS file: /cvsroot/src/sys/dev/scsipi/scsipi_all.h,v retrieving revision 1.32 diff -u -r1.32 scsipi_all.h --- sys/dev/scsipi/scsipi_all.h 1 Dec 2006 21:36:56 -0000 1.32 +++ sys/dev/scsipi/scsipi_all.h 10 Jan 2008 19:30:35 -0000 @@ -55,6 +55,8 @@ * Some basic, common SCSI commands */ +#define TEST_UNIT_READY 0x00 +#define REQUEST_SENSE 0x03 #define INQUIRY 0x12 struct scsipi_inquiry { u_int8_t opcode; Index: sys/dev/pci/ips.c =================================================================== RCS file: /cvsroot/src/sys/dev/pci/ips.c,v retrieving revision 1.32 diff -u -r0 ips.c --- /dev/null 2008-01-10 21:11:23.000000000 +0100 +++ sys/dev/pci/ips.c 2008-01-10 21:12:51.000000000 +0100 @@ -0,0 +1,1090 @@ +/* $OpenBSD: ips.c,v 1.30 2007/09/17 01:33:33 krw Exp $ */ + +/* + * Copyright (c) 2006, 2007 Alexander Yurchenko <grange%openbsd.org@localhost> + * + * 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 controller driver. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/queue.h> + +#include <machine/bus.h> + +#include <dev/scsipi/scsi_all.h> +#include <dev/scsipi/scsi_disk.h> +#include <dev/scsipi/scsipi_all.h> +#include <dev/scsipi/scsipi_disk.h> +#include <dev/scsipi/scsiconf.h> + +#include <dev/pci/pcidevs.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +#define IPS_DEBUG /* XXX: remove when driver becomes stable */ + +/* 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 15 +#define IPS_MAXCMDS 128 + +#define IPS_MAXFER (64 * 1024) +#define IPS_MAXSGS 16 +#define IPS_MAXCMDSZ (IPS_CMDSZ + IPS_MAXSGS * IPS_SGSZ) + +#define IPS_CMDSZ sizeof(struct ips_cmd) +#define IPS_SGSZ sizeof(struct ips_sg) +#define IPS_SECSZ 512 + +/* 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_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_EXT_DCDB 0x95 +#define IPS_CMD_EXT_DCDB_SG 0x96 +#define IPS_CMD_RWNVRAMPAGE 0xbc +#define IPS_CMD_GETVERINFO 0xc6 +#define IPS_CMD_FFDC 0xd7 +#define IPS_CMD_SG 0x80 + +/* 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_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 */ + +/* Copperhead definitions */ +#define IPS_COPPER_REG_SQTR 0x24 /* Status Queue Tail Reg. */ + +#define IPS_REG_STAT_ID(x) (((x) >> 8) & 0xff) +#define IPS_REG_STAT_BASIC(x) (((x) >> 16) & 0xff) +#define IPS_REG_STAT_GSC(x) (((x) >> 16) & 0x0f) +#define IPS_REG_STAT_EXT(x) (((x) >> 24) & 0xff) + +/* 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; +}; + +/* Scatter-gather array element */ +struct ips_sg { + u_int32_t addr; + u_int32_t size; +}; + +/* Copperhead queue entries */ +struct ips_copper_queue { + u_int32_t status[IPS_MAXCMDS]; + u_int32_t base_physaddr; + int nextstatus; + bus_dma_tag_t dmatag; + bus_dmamap_t dmamap; +}; + +/* 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 codeblkver[8]; + u_int8_t bootblkver[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 + 1)]; +}; + +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; + u_int32_t seccnt; + } drive[IPS_MAXDRIVES]; +}; + +/* Command control block */ +struct ips_ccb { + int c_id; /* command id */ + int c_flags; /* flags */ +#define IPS_CCB_READ 0x0001 +#define IPS_CCB_WRITE 0x0002 +#define IPS_CCB_POLL 0x0004 +#define IPS_CCB_RUN 0x0008 + + void * c_cmdva; /* command frame virt addr */ + paddr_t c_cmdpa; /* command frame phys addr */ + bus_dmamap_t c_dmam; /* data buffer DMA map */ + struct scsipi_xfer * c_xfer; /* corresponding SCSI xfer */ + int c_stat; /* status word copy */ + int c_estat; /* ext status word copy */ + + TAILQ_ENTRY(ips_ccb) c_link; /* queue link */ +}; + +/* CCB queue */ +TAILQ_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; + + struct scsibus_softc * sc_scsi_bus; + struct scsipi_adapter sc_adapter; + struct scsipi_channel sc_channel; + + 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_driveinfo sc_di; + int sc_nunits; + + struct dmamem sc_cmdm; + + struct ips_ccb * sc_ccb; + int sc_nccbs; + struct ips_ccbq sc_ccbq_free; + struct ips_ccbq sc_ccbq_run; + + struct ips_copper_queue *copper_queue; +}; + +int ips_match(struct device *, struct cfdata *, void *); +void ips_attach(struct device *, struct device *, void *); + +void ips_scsi_cmd(struct scsipi_channel *, scsipi_adapter_req_t, void *); + +int ips_cmd(struct ips_softc *, int, int, u_int32_t, void *, size_t, int, + struct scsipi_xfer *); +int ips_poll(struct ips_softc *, struct ips_ccb *); +void ips_done(struct ips_softc *, struct ips_ccb *); +int ips_intr(void *); + +int ips_getadapterinfo(struct ips_softc *, struct ips_adapterinfo *); +int ips_getdriveinfo(struct ips_softc *, struct ips_driveinfo *); +int ips_flush(struct ips_softc *); + +void ips_copperhead_exec(struct ips_softc *, struct ips_ccb *); +void ips_copperhead_init(struct ips_softc *); +void ips_copperhead_intren(struct ips_softc *); +int ips_copperhead_isintr(struct ips_softc *); +int ips_copperhead_reset(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_init(struct ips_softc *); +void ips_morpheus_intren(struct ips_softc *); +int ips_morpheus_isintr(struct ips_softc *); +int ips_morpheus_reset(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 *); + +CFATTACH_DECL(ips, sizeof(struct ips_softc), + ips_match, ips_attach, NULL, NULL); + +/* + * static struct scsipi_adapter ips_scsi_adapter = { + * .adapt_request = ips_scsi_cmd, + * .adapt_minphys = minphys, + * .adapt_ioctl = NULL, + * .adapt_enable = NULL, + * .adapt_getgeom = NULL, + * .adapt_accesschk = NULL + * }; + */ + +static const struct { + int vendor; + int product; +} 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 { + const char * ic_name; + int ic_bar; + + void (*ic_exec)(struct ips_softc *, struct ips_ccb *); + void (*ic_init)(struct ips_softc *); + void (*ic_intren)(struct ips_softc *); + int (*ic_isintr)(struct ips_softc *); + int (*ic_reset)(struct ips_softc *); + u_int32_t (*ic_status)(struct ips_softc *); +} ips_chips[] = { + { + "Copperhead", + 0x14, + ips_copperhead_exec, + ips_copperhead_init, + ips_copperhead_intren, + ips_copperhead_isintr, + ips_copperhead_reset, + ips_copperhead_status + }, + { + "Morpheus", + 0x10, + ips_morpheus_exec, + ips_morpheus_init, + ips_morpheus_intren, + ips_morpheus_isintr, + ips_morpheus_reset, + ips_morpheus_status + } +}; + +enum { + IPS_CHIP_COPPERHEAD = 0, + IPS_CHIP_MORPHEUS +}; + +#define ips_exec(s, c) (s)->sc_chip->ic_exec((s), (c)) +#define ips_init(s) (s)->sc_chip->ic_init((s)) +#define ips_intren(s) (s)->sc_chip->ic_intren((s)) +#define ips_isintr(s) (s)->sc_chip->ic_isintr((s)) +#define ips_reset(s) (s)->sc_chip->ic_reset((s)) +#define ips_status(s) (s)->sc_chip->ic_status((s)) + +int +ips_match(struct device *parent, struct cfdata *match, void *aux) +{ + struct pci_attach_args *pa = aux; + pcireg_t reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG); + int i; + + for (i = 0; ips_ids[i].vendor; i++) + { + if ((PCI_VENDOR(pa->pa_id) == ips_ids[i].vendor && + PCI_PRODUCT(pa->pa_id) == ips_ids[i].product) || + (PCI_VENDOR(reg) == ips_ids[i].vendor && + PCI_PRODUCT(reg) == ips_ids[i].product)) + 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; + pcireg_t maptype; + bus_size_t iosize; + pci_intr_handle_t ih; + const char *intrstr; + int i; + + sc->sc_dmat = pa->pa_dmat; + + /* Identify chipset */ + switch (PCI_PRODUCT(pa->pa_id)) { + case PCI_PRODUCT_IBM_SERVERAID: + sc->sc_chip = &ips_chips[IPS_CHIP_COPPERHEAD]; + break; + case PCI_PRODUCT_IBM_SERVERAID4: + case PCI_PRODUCT_ADP2_SERVERAID: + sc->sc_chip = &ips_chips[IPS_CHIP_MORPHEUS]; + break; + default: + printf(": unsupported chipset\n"); + return; + } + + /* Map registers */ + 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 registers\n"); + return; + } + + /* Initialize hardware */ + ips_init(sc); + + /* Allocate command buffer */ + if (ips_dmamem_alloc(&sc->sc_cmdm, sc->sc_dmat, + IPS_MAXCMDS * IPS_MAXCMDSZ)) { + printf(": can't allocate command buffer\n"); + goto fail1; + } + + /* Bootstrap CCB queue */ + sc->sc_nccbs = 1; + sc->sc_ccb = &ccb0; + bzero(&ccb0, sizeof(ccb0)); + ccb0.c_cmdva = sc->sc_cmdm.dm_vaddr; + ccb0.c_cmdpa = sc->sc_cmdm.dm_paddr; + if (bus_dmamap_create(sc->sc_dmat, IPS_MAXFER, IPS_MAXSGS, + IPS_MAXFER, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, + &ccb0.c_dmam)) { + printf(": can't bootstrap CCB queue\n"); + goto fail2; + } + TAILQ_INIT(&sc->sc_ccbq_free); + TAILQ_INIT(&sc->sc_ccbq_run); + TAILQ_INSERT_TAIL(&sc->sc_ccbq_free, &ccb0, c_link); + + /* Get adapter info */ + if (ips_getadapterinfo(sc, &ai)) { + printf(": can't get adapter info\n"); + bus_dmamap_destroy(sc->sc_dmat, ccb0.c_dmam); + goto fail2; + } + + /* Get logical drives info */ + if (ips_getdriveinfo(sc, &sc->sc_di)) { + printf(": can't get logical drives info\n"); + bus_dmamap_destroy(sc->sc_dmat, ccb0.c_dmam); + goto fail2; + } + sc->sc_nunits = sc->sc_di.drivecnt; + + bus_dmamap_destroy(sc->sc_dmat, ccb0.c_dmam); + + /* Initialize CCB queue */ + sc->sc_nccbs = ai.cmdcnt; + if ((sc->sc_ccb = ips_ccb_alloc(sc, sc->sc_nccbs)) == NULL) { + printf(": can't allocate CCB queue\n"); + goto fail2; + } + TAILQ_INIT(&sc->sc_ccbq_free); + TAILQ_INIT(&sc->sc_ccbq_run); + for (i = 0; i < sc->sc_nccbs; i++) + TAILQ_INSERT_TAIL(&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 fail3; + } + intrstr = pci_intr_string(pa->pa_pc, ih); + if (pci_intr_establish(pa->pa_pc, ih, IPL_BIO, ips_intr, sc) == NULL) + { + printf(": can't establish interrupt"); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + goto fail3; + } + printf(": %s\n", intrstr); + + /* Display adapter info */ + printf("%s", sc->sc_dev.dv_xname); + printf(": %s", sc->sc_chip->ic_name); + printf(", firmware %c%c%c%c%c%c%c", + ai.codeblkver[0], ai.codeblkver[1], ai.codeblkver[2], + ai.codeblkver[3], ai.codeblkver[4], ai.codeblkver[5], + ai.codeblkver[6]); + printf(", bootblock %c%c%c%c%c%c%c", + ai.bootblkver[0], ai.bootblkver[1], ai.bootblkver[2], + ai.bootblkver[3], ai.bootblkver[4], ai.bootblkver[5], + ai.bootblkver[6]); + printf(", %d CCBs, %d units", sc->sc_nccbs, sc->sc_nunits); + printf("\n"); + + /* Attach SCSI bus */ + if (sc->sc_nunits > 0) + { + sc->sc_channel.chan_openings = + sc->sc_adapter.adapt_openings = + sc->sc_nccbs / sc->sc_nunits; + } + + sc->sc_adapter.adapt_dev = (struct device *) sc; + sc->sc_adapter.adapt_max_periph = sc->sc_nunits; + sc->sc_adapter.adapt_request = &ips_scsi_cmd; + sc->sc_adapter.adapt_minphys = &minphys; + + sc->sc_channel.chan_adapter = &sc->sc_adapter; + sc->sc_channel.chan_bustype = &scsi_bustype; + sc->sc_channel.chan_channel = 0; + sc->sc_channel.chan_ntargets = sc->sc_nunits; + sc->sc_channel.chan_nluns = 8; + sc->sc_channel.chan_flags = 0; + sc->sc_channel.chan_id = sc->sc_nunits; + + /* sc->sc_scsi_link.device = &ips_scsi_device; */ + + sc->sc_scsi_bus = (struct scsibus_softc *) + config_found(&sc->sc_dev, &sc->sc_channel, scsiprint); + + if (sc->sc_scsi_bus) + aprint_normal("%s: child SCSI bus %s\n", sc->sc_dev.dv_xname, + sc->sc_scsi_bus->sc_dev.dv_xname); + else + aprint_normal("%s: No child SCSI bus\n", sc->sc_dev.dv_xname); + + /* Enable interrupts */ + ips_intren(sc); + + return; +fail3: + ips_ccb_free(sc, sc->sc_ccb, sc->sc_nccbs); +fail2: + ips_dmamem_free(&sc->sc_cmdm); +fail1: + bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize); +} + +void +ips_scsi_cmd(struct scsipi_channel *chan, scsipi_adapter_req_t req, void *arg) +{ + struct scsipi_xfer *xs = (struct scsipi_xfer *) arg; + struct scsipi_periph *periph = xs->xs_periph; + struct ips_softc *sc = + (struct ips_softc *) chan->chan_adapter->adapt_dev; + struct ips_drive *drive; + struct scsipi_inquiry_data *id; + struct scsipi_read_capacity_10_data *rcd; + struct scsi_sense_data *sd; + struct scsipi_rw_10 *rw; + struct scsipi_rw_16 *rwb; + int target = periph->periph_target; + u_int32_t blkno, blkcnt; + int cmd, error, flags, s; + + if (target >= sc->sc_nunits || periph->periph_lun != 0) { + DPRINTF(IPS_D_INFO, ("%s: invalid scsi command, " + "target %d, lun %d\n", sc->sc_dev.dv_xname, + target, periph->periph_lun)); + xs->error = XS_DRIVER_STUFFUP; + s = splbio(); + scsipi_done(xs); + splx(s); + return; + } + + s = splbio(); + drive = &sc->sc_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: + if (xs->cmdlen == sizeof(struct scsipi_rw_10)) { + u_int32_t length; + rw = (void *)xs->cmd; + length = _4btol(rw->length); + blkno = _3btol(rw->addr) & + (SRW_TOPADDR << 16 | 0xffff); + blkcnt = length ? length : 0x100; + } else { + rwb = (void *)xs->cmd; + blkno = _4btol(rwb->addr); + blkcnt = _2btol(rwb->length); + } + + if (blkno >= le32toh(drive->seccnt) || blkno + blkcnt > + le32toh(drive->seccnt)) { + DPRINTF(IPS_D_ERR, ("%s: invalid scsi command, " + "blkno %u, blkcnt %u\n", sc->sc_dev.dv_xname, + blkno, blkcnt)); + xs->error = XS_DRIVER_STUFFUP; + scsipi_done(xs); + break; + } + + if (xs->xs_control & XS_CTL_DATA_IN) { + cmd = IPS_CMD_READ; + flags = IPS_CCB_READ; + } else { + cmd = IPS_CMD_WRITE; + flags = IPS_CCB_WRITE; + } + if (xs->xs_control & XS_CTL_POLL) + flags |= IPS_CCB_POLL; + + if ((error = ips_cmd(sc, cmd, target, blkno, xs->data, + blkcnt * IPS_SECSZ, flags, xs))) { + if (error == ENOMEM) { + splx(s); + return; + } else if (flags & IPS_CCB_POLL) { + splx(s); + return; + } else { + xs->error = XS_DRIVER_STUFFUP; + scsipi_done(xs); + break; + } + } + + splx(s); + if (flags & IPS_CCB_POLL) + return; + else + return; + case INQUIRY: + id = (void *)xs->data; + bzero(id, sizeof(*id)); + id->device = T_DIRECT; + id->version = 2; + id->response_format = 2; + id->additional_length = 32; + strlcpy(id->vendor, "IBM ", sizeof(id->vendor)); + snprintf(id->product, sizeof(id->product), + "ServeRAID RAID%d #%02d", drive->raid, target); + strlcpy(id->revision, " ", sizeof(id->revision)); + break; + case READ_CAPACITY_10: + rcd = (void *)xs->data; + bzero(rcd, sizeof(*rcd)); + _lto4b(le32toh(drive->seccnt) - 1, rcd->addr); + _lto4b(IPS_SECSZ, rcd->length); + break; + case REQUEST_SENSE: + sd = (void *)xs->data; + bzero(sd, sizeof(*sd)); + sd->response_code = SSD_RCODE_CURRENT; + sd->flags = SKEY_NO_SENSE; + break; + case SCSI_SYNCHRONIZE_CACHE_10: + if (ips_flush(sc)) + xs->error = XS_DRIVER_STUFFUP; + break; + case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL: + case START_STOP: + case 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; + } + scsipi_done(xs); + splx(s); + + return; +} + +int +ips_cmd(struct ips_softc *sc, int code, int drive, u_int32_t lba, void *data, + size_t size, int flags, struct scsipi_xfer *xs) +{ + struct ips_cmd *cmd; + struct ips_sg *sg; + struct ips_ccb *ccb; + int nsegs, i, error = 0; + + DPRINTF(IPS_D_XFER, ("%s: cmd code 0x%02x, drive %d, lba %u, " + "size %lu, flags 0x%02x\n", sc->sc_dev.dv_xname, code, drive, lba, + (u_long)size, flags)); + + /* Grab free CCB */ + if ((ccb = ips_ccb_get(sc)) == NULL) { + DPRINTF(IPS_D_ERR, ("%s: no free CCB\n", sc->sc_dev.dv_xname)); + return (ENOMEM); + } + + ccb->c_flags = flags; + ccb->c_xfer = xs; + + /* Fill in command frame */ + cmd = ccb->c_cmdva; + cmd->code = code; + cmd->id = ccb->c_id; + cmd->drive = drive; + cmd->lba = htole32(lba); + cmd->seccnt = htole16(howmany(size, IPS_SECSZ)); + + if (size > 0) { + /* Map data buffer into DMA segments */ + if (bus_dmamap_load(sc->sc_dmat, ccb->c_dmam, data, size, + NULL, BUS_DMA_NOWAIT)) { + printf("%s: can't load DMA map\n", + sc->sc_dev.dv_xname); + return (1); /* XXX: return code */ + } + bus_dmamap_sync(sc->sc_dmat, ccb->c_dmam, 0, + ccb->c_dmam->dm_mapsize, + flags & IPS_CCB_READ ? BUS_DMASYNC_PREREAD : + BUS_DMASYNC_PREWRITE); + + if ((nsegs = ccb->c_dmam->dm_nsegs) > IPS_MAXSGS) { + printf("%s: too many DMA segments\n", + sc->sc_dev.dv_xname); + return (1); /* XXX: return code */ + } + + if (nsegs > 1) { + cmd->code |= IPS_CMD_SG; + cmd->sgcnt = nsegs; + cmd->sgaddr = htole32(ccb->c_cmdpa + IPS_CMDSZ); + + /* Fill in scatter-gather array */ + sg = (void *)(cmd + 1); + 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); + } + } + + /* Pass command to hardware */ + DPRINTF(IPS_D_XFER, ("%s: run command 0x%02x\n", sc->sc_dev.dv_xname, + ccb->c_id)); + ccb->c_flags |= IPS_CCB_RUN; + TAILQ_INSERT_TAIL(&sc->sc_ccbq_run, ccb, c_link); + ips_exec(sc, ccb); + + if (flags & IPS_CCB_POLL) + /* Wait for command to complete */ + error = ips_poll(sc, ccb); + + return (error); +} + +int +ips_poll(struct ips_softc *sc, struct ips_ccb *c) +{ + struct ips_ccb *ccb = NULL; + u_int32_t status; + int id, timeout; + + while (ccb != c) { + for (timeout = 100; timeout-- > 0; delay(100)) { + if ((status = ips_status(sc)) == 0xffffffff) + continue; + id = IPS_REG_STAT_ID(status); + if (id >= sc->sc_nccbs) { + DPRINTF(IPS_D_ERR, ("%s: invalid command " + "0x%02x\n", sc->sc_dev.dv_xname, id)); + continue; + } + break; + } + if (timeout < 0) { + printf("%s: poll timeout\n", sc->sc_dev.dv_xname); + return (EBUSY); + } + ccb = &sc->sc_ccb[id]; + ccb->c_stat = IPS_REG_STAT_GSC(status); + ccb->c_estat = IPS_REG_STAT_EXT(status); + ips_done(sc, ccb); + } + + return (0); +} + +void +ips_done(struct ips_softc *sc, struct ips_ccb *ccb) +{ + struct scsipi_xfer *xs = ccb->c_xfer; + int flags = ccb->c_flags; + int error = 0; + + if ((flags & IPS_CCB_RUN) == 0) { + printf("%s: command 0x%02x not run\n", sc->sc_dev.dv_xname, + ccb->c_id); + if (xs != NULL) { + xs->error = XS_DRIVER_STUFFUP; + scsipi_done(xs); + } + return; + } + + if (flags & (IPS_CCB_READ | IPS_CCB_WRITE)) { + bus_dmamap_sync(sc->sc_dmat, ccb->c_dmam, 0, + ccb->c_dmam->dm_mapsize, flags & IPS_CCB_READ ? + BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, ccb->c_dmam); + } + + if (ccb->c_stat) { + printf("%s: ", sc->sc_dev.dv_xname); + if (ccb->c_stat == 1) { + printf("recovered error\n"); + } else { + printf("error\n"); + error = 1; + } + } + + /* Release CCB */ + TAILQ_REMOVE(&sc->sc_ccbq_run, ccb, c_link); + ips_ccb_put(sc, ccb); + + if (xs != NULL) { + if (error) + xs->error = XS_DRIVER_STUFFUP; + else + xs->resid = 0; + xs->xs_status |= XS_STS_DONE; + scsipi_done(xs); + } +} + +int +ips_intr(void *arg) +{ + struct ips_softc *sc = arg; + struct ips_ccb *ccb; + u_int32_t status; + int id; + + if (!ips_isintr(sc)) + return (0); + + /* Process completed commands */ + while ((status = ips_status(sc)) != 0xffffffff) { + DPRINTF(IPS_D_XFER, ("%s: intr status 0x%08x\n", + sc->sc_dev.dv_xname, status)); + + id = IPS_REG_STAT_ID(status); + if (id >= sc->sc_nccbs) { + DPRINTF(IPS_D_ERR, ("%s: invalid command %d\n", + sc->sc_dev.dv_xname, id)); + continue; + } + ccb = &sc->sc_ccb[id]; + ccb->c_stat = IPS_REG_STAT_GSC(status); + ccb->c_estat = IPS_REG_STAT_EXT(status); + ips_done(sc, ccb); + } + + return (1); +} + +int +ips_getadapterinfo(struct ips_softc *sc, struct ips_adapterinfo *ai) +{ + return (ips_cmd(sc, IPS_CMD_GETADAPTERINFO, 0, 0, ai, sizeof(*ai), + IPS_CCB_READ | IPS_CCB_POLL, NULL)); +} + +int +ips_getdriveinfo(struct ips_softc *sc, struct ips_driveinfo *di) +{ + return (ips_cmd(sc, IPS_CMD_GETDRIVEINFO, 0, 0, di, sizeof(*di), + IPS_CCB_READ | IPS_CCB_POLL, NULL)); +} + +int +ips_flush(struct ips_softc *sc) +{ + return (ips_cmd(sc, IPS_CMD_FLUSH, 0, 0, NULL, 0, IPS_CCB_POLL, NULL)); +} + +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_cmdpa); + bus_space_write_2(sc->sc_iot, sc->sc_ioh, IPS_REG_CCC, + IPS_REG_CCC_START); +} + +void +ips_copperhead_init(struct ips_softc *sc) +{ + int ret; + bus_dma_tag_t dmat; + bus_dmamap_t map; + + /* XXX: not implemented */ +} + +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); +} + +int +ips_copperhead_reset(struct ips_softc *sc) +{ + /* XXX: not implemented */ + return (0); +} + +u_int32_t +ips_copperhead_status(struct ips_softc *sc) +{ + return (0); /* XXX: Not implemented yet */ +} + +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_cmdpa); +} + +void +ips_morpheus_init(struct ips_softc *sc) +{ + /* XXX: not implemented */ +} + +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) +{ + u_int32_t reg; + + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, IPS_REG_OIS); + DPRINTF(IPS_D_XFER, ("%s: isintr 0x%08x\n", sc->sc_dev.dv_xname, reg)); + + return (reg & IPS_REG_OIS_PEND); +} + +int +ips_morpheus_reset(struct ips_softc *sc) +{ + /* XXX: not implemented */ + return (0); +} + +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_id = i; + ccb[i].c_cmdva = (char *)sc->sc_cmdm.dm_vaddr + + i * IPS_MAXCMDSZ; + ccb[i].c_cmdpa = sc->sc_cmdm.dm_paddr + i * IPS_MAXCMDSZ; + 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; + + if ((ccb = TAILQ_FIRST(&sc->sc_ccbq_free)) != NULL) + TAILQ_REMOVE(&sc->sc_ccbq_free, ccb, c_link); + + return (ccb); +} + +void +ips_ccb_put(struct ips_softc *sc, struct ips_ccb *ccb) +{ + ccb->c_flags = 0; + ccb->c_xfer = NULL; + TAILQ_INSERT_TAIL(&sc->sc_ccbq_free, ccb, c_link); +} + +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, (caddr_t *)&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); +}
Attachment:
pgp85jWWoorZe.pgp
Description: PGP signature