Annotation of sys/dev/pci/ips.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: ips.c,v 1.29 2007/06/06 20:51:13 grange Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2006, 2007 Alexander Yurchenko <grange@openbsd.org>
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: */
! 18:
! 19: /*
! 20: * IBM (Adaptec) ServeRAID controller driver.
! 21: */
! 22:
! 23: #include <sys/param.h>
! 24: #include <sys/systm.h>
! 25: #include <sys/buf.h>
! 26: #include <sys/device.h>
! 27: #include <sys/kernel.h>
! 28: #include <sys/malloc.h>
! 29: #include <sys/timeout.h>
! 30: #include <sys/queue.h>
! 31:
! 32: #include <machine/bus.h>
! 33:
! 34: #include <scsi/scsi_all.h>
! 35: #include <scsi/scsi_disk.h>
! 36: #include <scsi/scsiconf.h>
! 37:
! 38: #include <dev/pci/pcidevs.h>
! 39: #include <dev/pci/pcireg.h>
! 40: #include <dev/pci/pcivar.h>
! 41:
! 42: #define IPS_DEBUG /* XXX: remove when driver becomes stable */
! 43:
! 44: /* Debug levels */
! 45: #define IPS_D_ERR 0x0001 /* errors */
! 46: #define IPS_D_INFO 0x0002 /* information */
! 47: #define IPS_D_XFER 0x0004 /* transfers */
! 48:
! 49: #ifdef IPS_DEBUG
! 50: #define DPRINTF(a, b) do { if (ips_debug & (a)) printf b; } while (0)
! 51: int ips_debug = IPS_D_ERR;
! 52: #else
! 53: #define DPRINTF(a, b)
! 54: #endif
! 55:
! 56: #define IPS_MAXDRIVES 8
! 57: #define IPS_MAXCHANS 4
! 58: #define IPS_MAXTARGETS 15
! 59: #define IPS_MAXCMDS 128
! 60:
! 61: #define IPS_MAXFER (64 * 1024)
! 62: #define IPS_MAXSGS 16
! 63: #define IPS_MAXCMDSZ (IPS_CMDSZ + IPS_MAXSGS * IPS_SGSZ)
! 64:
! 65: #define IPS_CMDSZ sizeof(struct ips_cmd)
! 66: #define IPS_SGSZ sizeof(struct ips_sg)
! 67: #define IPS_SECSZ 512
! 68:
! 69: /* Command codes */
! 70: #define IPS_CMD_READ 0x02
! 71: #define IPS_CMD_WRITE 0x03
! 72: #define IPS_CMD_DCDB 0x04
! 73: #define IPS_CMD_GETADAPTERINFO 0x05
! 74: #define IPS_CMD_FLUSH 0x0a
! 75: #define IPS_CMD_ERRORTABLE 0x17
! 76: #define IPS_CMD_GETDRIVEINFO 0x19
! 77: #define IPS_CMD_RESETCHAN 0x1a
! 78: #define IPS_CMD_DOWNLOAD 0x20
! 79: #define IPS_CMD_RWBIOSFW 0x22
! 80: #define IPS_CMD_READCONF 0x38
! 81: #define IPS_CMD_GETSUBSYS 0x40
! 82: #define IPS_CMD_CONFIGSYNC 0x58
! 83: #define IPS_CMD_READ_SG 0x82
! 84: #define IPS_CMD_WRITE_SG 0x83
! 85: #define IPS_CMD_DCDB_SG 0x84
! 86: #define IPS_CMD_EXT_DCDB 0x95
! 87: #define IPS_CMD_EXT_DCDB_SG 0x96
! 88: #define IPS_CMD_RWNVRAMPAGE 0xbc
! 89: #define IPS_CMD_GETVERINFO 0xc6
! 90: #define IPS_CMD_FFDC 0xd7
! 91: #define IPS_CMD_SG 0x80
! 92:
! 93: /* Register definitions */
! 94: #define IPS_REG_HIS 0x08 /* host interrupt status */
! 95: #define IPS_REG_HIS_SCE 0x01 /* status channel enqueue */
! 96: #define IPS_REG_HIS_EN 0x80 /* enable interrupts */
! 97: #define IPS_REG_CCSA 0x10 /* command channel system address */
! 98: #define IPS_REG_CCC 0x14 /* command channel control */
! 99: #define IPS_REG_CCC_SEM 0x0008 /* semaphore */
! 100: #define IPS_REG_CCC_START 0x101a /* start command */
! 101: #define IPS_REG_OIS 0x30 /* outbound interrupt status */
! 102: #define IPS_REG_OIS_PEND 0x0008 /* interrupt is pending */
! 103: #define IPS_REG_OIM 0x34 /* outbound interrupt mask */
! 104: #define IPS_REG_OIM_DS 0x0008 /* disable interrupts */
! 105: #define IPS_REG_IQP 0x40 /* inbound queue port */
! 106: #define IPS_REG_OQP 0x44 /* outbound queue port */
! 107:
! 108: #define IPS_REG_STAT_ID(x) (((x) >> 8) & 0xff)
! 109: #define IPS_REG_STAT_BASIC(x) (((x) >> 16) & 0xff)
! 110: #define IPS_REG_STAT_GSC(x) (((x) >> 16) & 0x0f)
! 111: #define IPS_REG_STAT_EXT(x) (((x) >> 24) & 0xff)
! 112:
! 113: /* Command frame */
! 114: struct ips_cmd {
! 115: u_int8_t code;
! 116: u_int8_t id;
! 117: u_int8_t drive;
! 118: u_int8_t sgcnt;
! 119: u_int32_t lba;
! 120: u_int32_t sgaddr;
! 121: u_int16_t seccnt;
! 122: u_int8_t seg4g;
! 123: u_int8_t esg;
! 124: u_int32_t ccsar;
! 125: u_int32_t cccr;
! 126: };
! 127:
! 128: /* Scatter-gather array element */
! 129: struct ips_sg {
! 130: u_int32_t addr;
! 131: u_int32_t size;
! 132: };
! 133:
! 134: /* Data frames */
! 135: struct ips_adapterinfo {
! 136: u_int8_t drivecnt;
! 137: u_int8_t miscflag;
! 138: u_int8_t sltflag;
! 139: u_int8_t bstflag;
! 140: u_int8_t pwrchgcnt;
! 141: u_int8_t wrongaddrcnt;
! 142: u_int8_t unidentcnt;
! 143: u_int8_t nvramdevchgcnt;
! 144: u_int8_t codeblkver[8];
! 145: u_int8_t bootblkver[8];
! 146: u_int32_t drivesize[IPS_MAXDRIVES];
! 147: u_int8_t cmdcnt;
! 148: u_int8_t maxphysdevs;
! 149: u_int16_t flashrepgmcnt;
! 150: u_int8_t defunctdiskcnt;
! 151: u_int8_t rebuildflag;
! 152: u_int8_t offdrivecnt;
! 153: u_int8_t critdrivecnt;
! 154: u_int16_t confupdcnt;
! 155: u_int8_t blkflag;
! 156: u_int8_t __reserved;
! 157: u_int16_t deaddisk[IPS_MAXCHANS * (IPS_MAXTARGETS + 1)];
! 158: };
! 159:
! 160: struct ips_driveinfo {
! 161: u_int8_t drivecnt;
! 162: u_int8_t __reserved[3];
! 163: struct ips_drive {
! 164: u_int8_t id;
! 165: u_int8_t __reserved;
! 166: u_int8_t raid;
! 167: u_int8_t state;
! 168: u_int32_t seccnt;
! 169: } drive[IPS_MAXDRIVES];
! 170: };
! 171:
! 172: /* Command control block */
! 173: struct ips_ccb {
! 174: int c_id; /* command id */
! 175: int c_flags; /* flags */
! 176: #define IPS_CCB_READ 0x0001
! 177: #define IPS_CCB_WRITE 0x0002
! 178: #define IPS_CCB_POLL 0x0004
! 179: #define IPS_CCB_RUN 0x0008
! 180:
! 181: void * c_cmdva; /* command frame virt addr */
! 182: paddr_t c_cmdpa; /* command frame phys addr */
! 183: bus_dmamap_t c_dmam; /* data buffer DMA map */
! 184: struct scsi_xfer * c_xfer; /* corresponding SCSI xfer */
! 185: int c_stat; /* status word copy */
! 186: int c_estat; /* ext status word copy */
! 187:
! 188: TAILQ_ENTRY(ips_ccb) c_link; /* queue link */
! 189: };
! 190:
! 191: /* CCB queue */
! 192: TAILQ_HEAD(ips_ccbq, ips_ccb);
! 193:
! 194: /* DMA-able chunk of memory */
! 195: struct dmamem {
! 196: bus_dma_tag_t dm_tag;
! 197: bus_dmamap_t dm_map;
! 198: bus_dma_segment_t dm_seg;
! 199: bus_size_t dm_size;
! 200: void * dm_vaddr;
! 201: #define dm_paddr dm_seg.ds_addr
! 202: };
! 203:
! 204: struct ips_softc {
! 205: struct device sc_dev;
! 206:
! 207: struct scsi_link sc_scsi_link;
! 208:
! 209: bus_space_tag_t sc_iot;
! 210: bus_space_handle_t sc_ioh;
! 211: bus_dma_tag_t sc_dmat;
! 212:
! 213: const struct ips_chipset *sc_chip;
! 214:
! 215: struct ips_driveinfo sc_di;
! 216: int sc_nunits;
! 217:
! 218: struct dmamem sc_cmdm;
! 219:
! 220: struct ips_ccb * sc_ccb;
! 221: int sc_nccbs;
! 222: struct ips_ccbq sc_ccbq_free;
! 223: struct ips_ccbq sc_ccbq_run;
! 224: };
! 225:
! 226: int ips_match(struct device *, void *, void *);
! 227: void ips_attach(struct device *, struct device *, void *);
! 228:
! 229: int ips_scsi_cmd(struct scsi_xfer *);
! 230:
! 231: int ips_cmd(struct ips_softc *, int, int, u_int32_t, void *, size_t, int,
! 232: struct scsi_xfer *);
! 233: int ips_poll(struct ips_softc *, struct ips_ccb *);
! 234: void ips_done(struct ips_softc *, struct ips_ccb *);
! 235: int ips_intr(void *);
! 236:
! 237: int ips_getadapterinfo(struct ips_softc *, struct ips_adapterinfo *);
! 238: int ips_getdriveinfo(struct ips_softc *, struct ips_driveinfo *);
! 239: int ips_flush(struct ips_softc *);
! 240:
! 241: void ips_copperhead_exec(struct ips_softc *, struct ips_ccb *);
! 242: void ips_copperhead_init(struct ips_softc *);
! 243: void ips_copperhead_intren(struct ips_softc *);
! 244: int ips_copperhead_isintr(struct ips_softc *);
! 245: int ips_copperhead_reset(struct ips_softc *);
! 246: u_int32_t ips_copperhead_status(struct ips_softc *);
! 247:
! 248: void ips_morpheus_exec(struct ips_softc *, struct ips_ccb *);
! 249: void ips_morpheus_init(struct ips_softc *);
! 250: void ips_morpheus_intren(struct ips_softc *);
! 251: int ips_morpheus_isintr(struct ips_softc *);
! 252: int ips_morpheus_reset(struct ips_softc *);
! 253: u_int32_t ips_morpheus_status(struct ips_softc *);
! 254:
! 255: struct ips_ccb *ips_ccb_alloc(struct ips_softc *, int);
! 256: void ips_ccb_free(struct ips_softc *, struct ips_ccb *, int);
! 257: struct ips_ccb *ips_ccb_get(struct ips_softc *);
! 258: void ips_ccb_put(struct ips_softc *, struct ips_ccb *);
! 259:
! 260: int ips_dmamem_alloc(struct dmamem *, bus_dma_tag_t, bus_size_t);
! 261: void ips_dmamem_free(struct dmamem *);
! 262:
! 263: struct cfattach ips_ca = {
! 264: sizeof(struct ips_softc),
! 265: ips_match,
! 266: ips_attach
! 267: };
! 268:
! 269: struct cfdriver ips_cd = {
! 270: NULL, "ips", DV_DULL
! 271: };
! 272:
! 273: static struct scsi_adapter ips_scsi_adapter = {
! 274: ips_scsi_cmd,
! 275: minphys,
! 276: NULL,
! 277: NULL,
! 278: NULL
! 279: };
! 280:
! 281: static struct scsi_device ips_scsi_device = {
! 282: NULL,
! 283: NULL,
! 284: NULL,
! 285: NULL
! 286: };
! 287:
! 288: static const struct pci_matchid ips_ids[] = {
! 289: { PCI_VENDOR_IBM, PCI_PRODUCT_IBM_SERVERAID },
! 290: { PCI_VENDOR_IBM, PCI_PRODUCT_IBM_SERVERAID2 },
! 291: { PCI_VENDOR_ADP2, PCI_PRODUCT_ADP2_SERVERAID }
! 292: };
! 293:
! 294: static const struct ips_chipset {
! 295: const char * ic_name;
! 296: int ic_bar;
! 297:
! 298: void (*ic_exec)(struct ips_softc *, struct ips_ccb *);
! 299: void (*ic_init)(struct ips_softc *);
! 300: void (*ic_intren)(struct ips_softc *);
! 301: int (*ic_isintr)(struct ips_softc *);
! 302: int (*ic_reset)(struct ips_softc *);
! 303: u_int32_t (*ic_status)(struct ips_softc *);
! 304: } ips_chips[] = {
! 305: {
! 306: "Copperhead",
! 307: 0x14,
! 308: ips_copperhead_exec,
! 309: ips_copperhead_init,
! 310: ips_copperhead_intren,
! 311: ips_copperhead_isintr,
! 312: ips_copperhead_reset,
! 313: ips_copperhead_status
! 314: },
! 315: {
! 316: "Morpheus",
! 317: 0x10,
! 318: ips_morpheus_exec,
! 319: ips_morpheus_init,
! 320: ips_morpheus_intren,
! 321: ips_morpheus_isintr,
! 322: ips_morpheus_reset,
! 323: ips_morpheus_status
! 324: }
! 325: };
! 326:
! 327: enum {
! 328: IPS_CHIP_COPPERHEAD = 0,
! 329: IPS_CHIP_MORPHEUS
! 330: };
! 331:
! 332: #define ips_exec(s, c) (s)->sc_chip->ic_exec((s), (c))
! 333: #define ips_init(s) (s)->sc_chip->ic_init((s))
! 334: #define ips_intren(s) (s)->sc_chip->ic_intren((s))
! 335: #define ips_isintr(s) (s)->sc_chip->ic_isintr((s))
! 336: #define ips_reset(s) (s)->sc_chip->ic_reset((s))
! 337: #define ips_status(s) (s)->sc_chip->ic_status((s))
! 338:
! 339: int
! 340: ips_match(struct device *parent, void *match, void *aux)
! 341: {
! 342: return (pci_matchbyid(aux, ips_ids,
! 343: sizeof(ips_ids) / sizeof(ips_ids[0])));
! 344: }
! 345:
! 346: void
! 347: ips_attach(struct device *parent, struct device *self, void *aux)
! 348: {
! 349: struct ips_softc *sc = (struct ips_softc *)self;
! 350: struct pci_attach_args *pa = aux;
! 351: struct ips_ccb ccb0;
! 352: struct scsibus_attach_args saa;
! 353: struct ips_adapterinfo ai;
! 354: pcireg_t maptype;
! 355: bus_size_t iosize;
! 356: pci_intr_handle_t ih;
! 357: const char *intrstr;
! 358: int i;
! 359:
! 360: sc->sc_dmat = pa->pa_dmat;
! 361:
! 362: /* Identify chipset */
! 363: switch (PCI_PRODUCT(pa->pa_id)) {
! 364: case PCI_PRODUCT_IBM_SERVERAID:
! 365: sc->sc_chip = &ips_chips[IPS_CHIP_COPPERHEAD];
! 366: break;
! 367: case PCI_PRODUCT_IBM_SERVERAID2:
! 368: case PCI_PRODUCT_ADP2_SERVERAID:
! 369: sc->sc_chip = &ips_chips[IPS_CHIP_MORPHEUS];
! 370: break;
! 371: default:
! 372: printf(": unsupported chipset\n");
! 373: return;
! 374: }
! 375:
! 376: /* Map registers */
! 377: maptype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, sc->sc_chip->ic_bar);
! 378: if (pci_mapreg_map(pa, sc->sc_chip->ic_bar, maptype, 0, &sc->sc_iot,
! 379: &sc->sc_ioh, NULL, &iosize, 0)) {
! 380: printf(": can't map registers\n");
! 381: return;
! 382: }
! 383:
! 384: /* Initialize hardware */
! 385: ips_init(sc);
! 386:
! 387: /* Allocate command buffer */
! 388: if (ips_dmamem_alloc(&sc->sc_cmdm, sc->sc_dmat,
! 389: IPS_MAXCMDS * IPS_MAXCMDSZ)) {
! 390: printf(": can't allocate command buffer\n");
! 391: goto fail1;
! 392: }
! 393:
! 394: /* Bootstrap CCB queue */
! 395: sc->sc_nccbs = 1;
! 396: sc->sc_ccb = &ccb0;
! 397: bzero(&ccb0, sizeof(ccb0));
! 398: ccb0.c_cmdva = sc->sc_cmdm.dm_vaddr;
! 399: ccb0.c_cmdpa = sc->sc_cmdm.dm_paddr;
! 400: if (bus_dmamap_create(sc->sc_dmat, IPS_MAXFER, IPS_MAXSGS,
! 401: IPS_MAXFER, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
! 402: &ccb0.c_dmam)) {
! 403: printf(": can't bootstrap CCB queue\n");
! 404: goto fail2;
! 405: }
! 406: TAILQ_INIT(&sc->sc_ccbq_free);
! 407: TAILQ_INIT(&sc->sc_ccbq_run);
! 408: TAILQ_INSERT_TAIL(&sc->sc_ccbq_free, &ccb0, c_link);
! 409:
! 410: /* Get adapter info */
! 411: if (ips_getadapterinfo(sc, &ai)) {
! 412: printf(": can't get adapter info\n");
! 413: bus_dmamap_destroy(sc->sc_dmat, ccb0.c_dmam);
! 414: goto fail2;
! 415: }
! 416:
! 417: /* Get logical drives info */
! 418: if (ips_getdriveinfo(sc, &sc->sc_di)) {
! 419: printf(": can't get logical drives info\n");
! 420: bus_dmamap_destroy(sc->sc_dmat, ccb0.c_dmam);
! 421: goto fail2;
! 422: }
! 423: sc->sc_nunits = sc->sc_di.drivecnt;
! 424:
! 425: bus_dmamap_destroy(sc->sc_dmat, ccb0.c_dmam);
! 426:
! 427: /* Initialize CCB queue */
! 428: sc->sc_nccbs = ai.cmdcnt;
! 429: if ((sc->sc_ccb = ips_ccb_alloc(sc, sc->sc_nccbs)) == NULL) {
! 430: printf(": can't allocate CCB queue\n");
! 431: goto fail2;
! 432: }
! 433: TAILQ_INIT(&sc->sc_ccbq_free);
! 434: TAILQ_INIT(&sc->sc_ccbq_run);
! 435: for (i = 0; i < sc->sc_nccbs; i++)
! 436: TAILQ_INSERT_TAIL(&sc->sc_ccbq_free,
! 437: &sc->sc_ccb[i], c_link);
! 438:
! 439: /* Install interrupt handler */
! 440: if (pci_intr_map(pa, &ih)) {
! 441: printf(": can't map interrupt\n");
! 442: goto fail3;
! 443: }
! 444: intrstr = pci_intr_string(pa->pa_pc, ih);
! 445: if (pci_intr_establish(pa->pa_pc, ih, IPL_BIO, ips_intr, sc,
! 446: sc->sc_dev.dv_xname) == NULL) {
! 447: printf(": can't establish interrupt");
! 448: if (intrstr != NULL)
! 449: printf(" at %s", intrstr);
! 450: printf("\n");
! 451: goto fail3;
! 452: }
! 453: printf(": %s\n", intrstr);
! 454:
! 455: /* Display adapter info */
! 456: printf("%s", sc->sc_dev.dv_xname);
! 457: printf(": %s", sc->sc_chip->ic_name);
! 458: printf(", firmware %c%c%c%c%c%c%c",
! 459: ai.codeblkver[0], ai.codeblkver[1], ai.codeblkver[2],
! 460: ai.codeblkver[3], ai.codeblkver[4], ai.codeblkver[5],
! 461: ai.codeblkver[6]);
! 462: printf(", bootblock %c%c%c%c%c%c%c",
! 463: ai.bootblkver[0], ai.bootblkver[1], ai.bootblkver[2],
! 464: ai.bootblkver[3], ai.bootblkver[4], ai.bootblkver[5],
! 465: ai.bootblkver[6]);
! 466: printf(", %d CCBs, %d units", sc->sc_nccbs, sc->sc_nunits);
! 467: printf("\n");
! 468:
! 469: /* Attach SCSI bus */
! 470: if (sc->sc_nunits > 0)
! 471: sc->sc_scsi_link.openings = sc->sc_nccbs / sc->sc_nunits;
! 472: sc->sc_scsi_link.adapter_target = sc->sc_nunits;
! 473: sc->sc_scsi_link.adapter_buswidth = sc->sc_nunits;
! 474: sc->sc_scsi_link.device = &ips_scsi_device;
! 475: sc->sc_scsi_link.adapter = &ips_scsi_adapter;
! 476: sc->sc_scsi_link.adapter_softc = sc;
! 477:
! 478: bzero(&saa, sizeof(saa));
! 479: saa.saa_sc_link = &sc->sc_scsi_link;
! 480: config_found(self, &saa, scsiprint);
! 481:
! 482: /* Enable interrupts */
! 483: ips_intren(sc);
! 484:
! 485: return;
! 486: fail3:
! 487: ips_ccb_free(sc, sc->sc_ccb, sc->sc_nccbs);
! 488: fail2:
! 489: ips_dmamem_free(&sc->sc_cmdm);
! 490: fail1:
! 491: bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
! 492: }
! 493:
! 494: int
! 495: ips_scsi_cmd(struct scsi_xfer *xs)
! 496: {
! 497: struct scsi_link *link = xs->sc_link;
! 498: struct ips_softc *sc = link->adapter_softc;
! 499: struct ips_drive *drive;
! 500: struct scsi_inquiry_data *id;
! 501: struct scsi_read_cap_data *rcd;
! 502: struct scsi_sense_data *sd;
! 503: struct scsi_rw *rw;
! 504: struct scsi_rw_big *rwb;
! 505: int target = link->target;
! 506: u_int32_t blkno, blkcnt;
! 507: int cmd, error, flags, s;
! 508:
! 509: if (target >= sc->sc_nunits || link->lun != 0) {
! 510: DPRINTF(IPS_D_INFO, ("%s: invalid scsi command, "
! 511: "target %d, lun %d\n", sc->sc_dev.dv_xname,
! 512: target, link->lun));
! 513: xs->error = XS_DRIVER_STUFFUP;
! 514: s = splbio();
! 515: scsi_done(xs);
! 516: splx(s);
! 517: return (COMPLETE);
! 518: }
! 519:
! 520: s = splbio();
! 521: drive = &sc->sc_di.drive[target];
! 522: xs->error = XS_NOERROR;
! 523:
! 524: /* Fake SCSI commands */
! 525: switch (xs->cmd->opcode) {
! 526: case READ_BIG:
! 527: case READ_COMMAND:
! 528: case WRITE_BIG:
! 529: case WRITE_COMMAND:
! 530: if (xs->cmdlen == sizeof(struct scsi_rw)) {
! 531: rw = (void *)xs->cmd;
! 532: blkno = _3btol(rw->addr) &
! 533: (SRW_TOPADDR << 16 | 0xffff);
! 534: blkcnt = rw->length ? rw->length : 0x100;
! 535: } else {
! 536: rwb = (void *)xs->cmd;
! 537: blkno = _4btol(rwb->addr);
! 538: blkcnt = _2btol(rwb->length);
! 539: }
! 540:
! 541: if (blkno >= letoh32(drive->seccnt) || blkno + blkcnt >
! 542: letoh32(drive->seccnt)) {
! 543: DPRINTF(IPS_D_ERR, ("%s: invalid scsi command, "
! 544: "blkno %u, blkcnt %u\n", sc->sc_dev.dv_xname,
! 545: blkno, blkcnt));
! 546: xs->error = XS_DRIVER_STUFFUP;
! 547: scsi_done(xs);
! 548: break;
! 549: }
! 550:
! 551: if (xs->flags & SCSI_DATA_IN) {
! 552: cmd = IPS_CMD_READ;
! 553: flags = IPS_CCB_READ;
! 554: } else {
! 555: cmd = IPS_CMD_WRITE;
! 556: flags = IPS_CCB_WRITE;
! 557: }
! 558: if (xs->flags & SCSI_POLL)
! 559: flags |= IPS_CCB_POLL;
! 560:
! 561: if ((error = ips_cmd(sc, cmd, target, blkno, xs->data,
! 562: blkcnt * IPS_SECSZ, flags, xs))) {
! 563: if (error == ENOMEM) {
! 564: splx(s);
! 565: return (NO_CCB);
! 566: } else if (flags & IPS_CCB_POLL) {
! 567: splx(s);
! 568: return (TRY_AGAIN_LATER);
! 569: } else {
! 570: xs->error = XS_DRIVER_STUFFUP;
! 571: scsi_done(xs);
! 572: break;
! 573: }
! 574: }
! 575:
! 576: splx(s);
! 577: if (flags & IPS_CCB_POLL)
! 578: return (COMPLETE);
! 579: else
! 580: return (SUCCESSFULLY_QUEUED);
! 581: case INQUIRY:
! 582: id = (void *)xs->data;
! 583: bzero(id, sizeof(*id));
! 584: id->device = T_DIRECT;
! 585: id->version = 2;
! 586: id->response_format = 2;
! 587: id->additional_length = 32;
! 588: strlcpy(id->vendor, "IBM ", sizeof(id->vendor));
! 589: snprintf(id->product, sizeof(id->product),
! 590: "ServeRAID RAID%d #%02d", drive->raid, target);
! 591: strlcpy(id->revision, " ", sizeof(id->revision));
! 592: break;
! 593: case READ_CAPACITY:
! 594: rcd = (void *)xs->data;
! 595: bzero(rcd, sizeof(*rcd));
! 596: _lto4b(letoh32(drive->seccnt) - 1, rcd->addr);
! 597: _lto4b(IPS_SECSZ, rcd->length);
! 598: break;
! 599: case REQUEST_SENSE:
! 600: sd = (void *)xs->data;
! 601: bzero(sd, sizeof(*sd));
! 602: sd->error_code = SSD_ERRCODE_CURRENT;
! 603: sd->flags = SKEY_NO_SENSE;
! 604: break;
! 605: case SYNCHRONIZE_CACHE:
! 606: if (ips_flush(sc))
! 607: xs->error = XS_DRIVER_STUFFUP;
! 608: break;
! 609: case PREVENT_ALLOW:
! 610: case START_STOP:
! 611: case TEST_UNIT_READY:
! 612: break;
! 613: default:
! 614: DPRINTF(IPS_D_INFO, ("%s: unsupported scsi command 0x%02x\n",
! 615: sc->sc_dev.dv_xname, xs->cmd->opcode));
! 616: xs->error = XS_DRIVER_STUFFUP;
! 617: }
! 618: scsi_done(xs);
! 619: splx(s);
! 620:
! 621: return (COMPLETE);
! 622: }
! 623:
! 624: int
! 625: ips_cmd(struct ips_softc *sc, int code, int drive, u_int32_t lba, void *data,
! 626: size_t size, int flags, struct scsi_xfer *xs)
! 627: {
! 628: struct ips_cmd *cmd;
! 629: struct ips_sg *sg;
! 630: struct ips_ccb *ccb;
! 631: int nsegs, i, error = 0;
! 632:
! 633: DPRINTF(IPS_D_XFER, ("%s: cmd code 0x%02x, drive %d, lba %u, "
! 634: "size %lu, flags 0x%02x\n", sc->sc_dev.dv_xname, code, drive, lba,
! 635: (u_long)size, flags));
! 636:
! 637: /* Grab free CCB */
! 638: if ((ccb = ips_ccb_get(sc)) == NULL) {
! 639: DPRINTF(IPS_D_ERR, ("%s: no free CCB\n", sc->sc_dev.dv_xname));
! 640: return (ENOMEM);
! 641: }
! 642:
! 643: ccb->c_flags = flags;
! 644: ccb->c_xfer = xs;
! 645:
! 646: /* Fill in command frame */
! 647: cmd = ccb->c_cmdva;
! 648: cmd->code = code;
! 649: cmd->id = ccb->c_id;
! 650: cmd->drive = drive;
! 651: cmd->lba = htole32(lba);
! 652: cmd->seccnt = htole16(howmany(size, IPS_SECSZ));
! 653:
! 654: if (size > 0) {
! 655: /* Map data buffer into DMA segments */
! 656: if (bus_dmamap_load(sc->sc_dmat, ccb->c_dmam, data, size,
! 657: NULL, BUS_DMA_NOWAIT)) {
! 658: printf("%s: can't load DMA map\n",
! 659: sc->sc_dev.dv_xname);
! 660: return (1); /* XXX: return code */
! 661: }
! 662: bus_dmamap_sync(sc->sc_dmat, ccb->c_dmam, 0,
! 663: ccb->c_dmam->dm_mapsize,
! 664: flags & IPS_CCB_READ ? BUS_DMASYNC_PREREAD :
! 665: BUS_DMASYNC_PREWRITE);
! 666:
! 667: if ((nsegs = ccb->c_dmam->dm_nsegs) > IPS_MAXSGS) {
! 668: printf("%s: too many DMA segments\n",
! 669: sc->sc_dev.dv_xname);
! 670: return (1); /* XXX: return code */
! 671: }
! 672:
! 673: if (nsegs > 1) {
! 674: cmd->code |= IPS_CMD_SG;
! 675: cmd->sgcnt = nsegs;
! 676: cmd->sgaddr = htole32(ccb->c_cmdpa + IPS_CMDSZ);
! 677:
! 678: /* Fill in scatter-gather array */
! 679: sg = (void *)(cmd + 1);
! 680: for (i = 0; i < nsegs; i++) {
! 681: sg[i].addr =
! 682: htole32(ccb->c_dmam->dm_segs[i].ds_addr);
! 683: sg[i].size =
! 684: htole32(ccb->c_dmam->dm_segs[i].ds_len);
! 685: }
! 686: } else {
! 687: cmd->sgcnt = 0;
! 688: cmd->sgaddr = htole32(ccb->c_dmam->dm_segs[0].ds_addr);
! 689: }
! 690: }
! 691:
! 692: /* Pass command to hardware */
! 693: DPRINTF(IPS_D_XFER, ("%s: run command 0x%02x\n", sc->sc_dev.dv_xname,
! 694: ccb->c_id));
! 695: ccb->c_flags |= IPS_CCB_RUN;
! 696: TAILQ_INSERT_TAIL(&sc->sc_ccbq_run, ccb, c_link);
! 697: ips_exec(sc, ccb);
! 698:
! 699: if (flags & IPS_CCB_POLL)
! 700: /* Wait for command to complete */
! 701: error = ips_poll(sc, ccb);
! 702:
! 703: return (error);
! 704: }
! 705:
! 706: int
! 707: ips_poll(struct ips_softc *sc, struct ips_ccb *c)
! 708: {
! 709: struct ips_ccb *ccb = NULL;
! 710: u_int32_t status;
! 711: int id, timeout;
! 712:
! 713: while (ccb != c) {
! 714: for (timeout = 100; timeout-- > 0; delay(100)) {
! 715: if ((status = ips_status(sc)) == 0xffffffff)
! 716: continue;
! 717: id = IPS_REG_STAT_ID(status);
! 718: if (id >= sc->sc_nccbs) {
! 719: DPRINTF(IPS_D_ERR, ("%s: invalid command "
! 720: "0x%02x\n", sc->sc_dev.dv_xname, id));
! 721: continue;
! 722: }
! 723: break;
! 724: }
! 725: if (timeout < 0) {
! 726: printf("%s: poll timeout\n", sc->sc_dev.dv_xname);
! 727: return (EBUSY);
! 728: }
! 729: ccb = &sc->sc_ccb[id];
! 730: ccb->c_stat = IPS_REG_STAT_GSC(status);
! 731: ccb->c_estat = IPS_REG_STAT_EXT(status);
! 732: ips_done(sc, ccb);
! 733: }
! 734:
! 735: return (0);
! 736: }
! 737:
! 738: void
! 739: ips_done(struct ips_softc *sc, struct ips_ccb *ccb)
! 740: {
! 741: struct scsi_xfer *xs = ccb->c_xfer;
! 742: int flags = ccb->c_flags;
! 743: int error = 0;
! 744:
! 745: if ((flags & IPS_CCB_RUN) == 0) {
! 746: printf("%s: command 0x%02x not run\n", sc->sc_dev.dv_xname,
! 747: ccb->c_id);
! 748: if (xs != NULL) {
! 749: xs->error = XS_DRIVER_STUFFUP;
! 750: scsi_done(xs);
! 751: }
! 752: return;
! 753: }
! 754:
! 755: if (flags & (IPS_CCB_READ | IPS_CCB_WRITE)) {
! 756: bus_dmamap_sync(sc->sc_dmat, ccb->c_dmam, 0,
! 757: ccb->c_dmam->dm_mapsize, flags & IPS_CCB_READ ?
! 758: BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
! 759: bus_dmamap_unload(sc->sc_dmat, ccb->c_dmam);
! 760: }
! 761:
! 762: if (ccb->c_stat) {
! 763: printf("%s: ", sc->sc_dev.dv_xname);
! 764: if (ccb->c_stat == 1) {
! 765: printf("recovered error\n");
! 766: } else {
! 767: printf("error\n");
! 768: error = 1;
! 769: }
! 770: }
! 771:
! 772: /* Release CCB */
! 773: TAILQ_REMOVE(&sc->sc_ccbq_run, ccb, c_link);
! 774: ips_ccb_put(sc, ccb);
! 775:
! 776: if (xs != NULL) {
! 777: if (error)
! 778: xs->error = XS_DRIVER_STUFFUP;
! 779: else
! 780: xs->resid = 0;
! 781: xs->flags |= ITSDONE;
! 782: scsi_done(xs);
! 783: }
! 784: }
! 785:
! 786: int
! 787: ips_intr(void *arg)
! 788: {
! 789: struct ips_softc *sc = arg;
! 790: struct ips_ccb *ccb;
! 791: u_int32_t status;
! 792: int id;
! 793:
! 794: if (!ips_isintr(sc))
! 795: return (0);
! 796:
! 797: /* Process completed commands */
! 798: while ((status = ips_status(sc)) != 0xffffffff) {
! 799: DPRINTF(IPS_D_XFER, ("%s: intr status 0x%08x\n",
! 800: sc->sc_dev.dv_xname, status));
! 801:
! 802: id = IPS_REG_STAT_ID(status);
! 803: if (id >= sc->sc_nccbs) {
! 804: DPRINTF(IPS_D_ERR, ("%s: invalid command %d\n",
! 805: sc->sc_dev.dv_xname, id));
! 806: continue;
! 807: }
! 808: ccb = &sc->sc_ccb[id];
! 809: ccb->c_stat = IPS_REG_STAT_GSC(status);
! 810: ccb->c_estat = IPS_REG_STAT_EXT(status);
! 811: ips_done(sc, ccb);
! 812: }
! 813:
! 814: return (1);
! 815: }
! 816:
! 817: int
! 818: ips_getadapterinfo(struct ips_softc *sc, struct ips_adapterinfo *ai)
! 819: {
! 820: return (ips_cmd(sc, IPS_CMD_GETADAPTERINFO, 0, 0, ai, sizeof(*ai),
! 821: IPS_CCB_READ | IPS_CCB_POLL, NULL));
! 822: }
! 823:
! 824: int
! 825: ips_getdriveinfo(struct ips_softc *sc, struct ips_driveinfo *di)
! 826: {
! 827: return (ips_cmd(sc, IPS_CMD_GETDRIVEINFO, 0, 0, di, sizeof(*di),
! 828: IPS_CCB_READ | IPS_CCB_POLL, NULL));
! 829: }
! 830:
! 831: int
! 832: ips_flush(struct ips_softc *sc)
! 833: {
! 834: return (ips_cmd(sc, IPS_CMD_FLUSH, 0, 0, NULL, 0, IPS_CCB_POLL, NULL));
! 835: }
! 836:
! 837: void
! 838: ips_copperhead_exec(struct ips_softc *sc, struct ips_ccb *ccb)
! 839: {
! 840: u_int32_t reg;
! 841: int timeout;
! 842:
! 843: for (timeout = 100; timeout-- > 0; delay(100)) {
! 844: reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, IPS_REG_CCC);
! 845: if ((reg & IPS_REG_CCC_SEM) == 0)
! 846: break;
! 847: }
! 848: if (timeout < 0) {
! 849: printf("%s: semaphore timeout\n", sc->sc_dev.dv_xname);
! 850: return;
! 851: }
! 852:
! 853: bus_space_write_4(sc->sc_iot, sc->sc_ioh, IPS_REG_CCSA, ccb->c_cmdpa);
! 854: bus_space_write_2(sc->sc_iot, sc->sc_ioh, IPS_REG_CCC,
! 855: IPS_REG_CCC_START);
! 856: }
! 857:
! 858: void
! 859: ips_copperhead_init(struct ips_softc *sc)
! 860: {
! 861: /* XXX: not implemented */
! 862: }
! 863:
! 864: void
! 865: ips_copperhead_intren(struct ips_softc *sc)
! 866: {
! 867: bus_space_write_1(sc->sc_iot, sc->sc_ioh, IPS_REG_HIS, IPS_REG_HIS_EN);
! 868: }
! 869:
! 870: int
! 871: ips_copperhead_isintr(struct ips_softc *sc)
! 872: {
! 873: u_int8_t reg;
! 874:
! 875: reg = bus_space_read_1(sc->sc_iot, sc->sc_ioh, IPS_REG_HIS);
! 876: bus_space_write_1(sc->sc_iot, sc->sc_ioh, IPS_REG_HIS, reg);
! 877: if (reg != 0xff && (reg & IPS_REG_HIS_SCE))
! 878: return (1);
! 879:
! 880: return (0);
! 881: }
! 882:
! 883: int
! 884: ips_copperhead_reset(struct ips_softc *sc)
! 885: {
! 886: /* XXX: not implemented */
! 887: return (0);
! 888: }
! 889:
! 890: u_int32_t
! 891: ips_copperhead_status(struct ips_softc *sc)
! 892: {
! 893: /* XXX: not implemented */
! 894: return (0);
! 895: }
! 896:
! 897: void
! 898: ips_morpheus_exec(struct ips_softc *sc, struct ips_ccb *ccb)
! 899: {
! 900: bus_space_write_4(sc->sc_iot, sc->sc_ioh, IPS_REG_IQP, ccb->c_cmdpa);
! 901: }
! 902:
! 903: void
! 904: ips_morpheus_init(struct ips_softc *sc)
! 905: {
! 906: /* XXX: not implemented */
! 907: }
! 908:
! 909: void
! 910: ips_morpheus_intren(struct ips_softc *sc)
! 911: {
! 912: u_int32_t reg;
! 913:
! 914: reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, IPS_REG_OIM);
! 915: reg &= ~IPS_REG_OIM_DS;
! 916: bus_space_write_4(sc->sc_iot, sc->sc_ioh, IPS_REG_OIM, reg);
! 917: }
! 918:
! 919: int
! 920: ips_morpheus_isintr(struct ips_softc *sc)
! 921: {
! 922: u_int32_t reg;
! 923:
! 924: reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, IPS_REG_OIS);
! 925: DPRINTF(IPS_D_XFER, ("%s: isintr 0x%08x\n", sc->sc_dev.dv_xname, reg));
! 926:
! 927: return (reg & IPS_REG_OIS_PEND);
! 928: }
! 929:
! 930: int
! 931: ips_morpheus_reset(struct ips_softc *sc)
! 932: {
! 933: /* XXX: not implemented */
! 934: return (0);
! 935: }
! 936:
! 937: u_int32_t
! 938: ips_morpheus_status(struct ips_softc *sc)
! 939: {
! 940: u_int32_t reg;
! 941:
! 942: reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, IPS_REG_OQP);
! 943: DPRINTF(IPS_D_XFER, ("%s: status 0x%08x\n", sc->sc_dev.dv_xname, reg));
! 944:
! 945: return (reg);
! 946: }
! 947:
! 948: struct ips_ccb *
! 949: ips_ccb_alloc(struct ips_softc *sc, int n)
! 950: {
! 951: struct ips_ccb *ccb;
! 952: int i;
! 953:
! 954: if ((ccb = malloc(n * sizeof(*ccb), M_DEVBUF, M_NOWAIT)) == NULL)
! 955: return (NULL);
! 956: bzero(ccb, n * sizeof(*ccb));
! 957:
! 958: for (i = 0; i < n; i++) {
! 959: ccb[i].c_id = i;
! 960: ccb[i].c_cmdva = (char *)sc->sc_cmdm.dm_vaddr +
! 961: i * IPS_MAXCMDSZ;
! 962: ccb[i].c_cmdpa = sc->sc_cmdm.dm_paddr + i * IPS_MAXCMDSZ;
! 963: if (bus_dmamap_create(sc->sc_dmat, IPS_MAXFER, IPS_MAXSGS,
! 964: IPS_MAXFER, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
! 965: &ccb[i].c_dmam))
! 966: goto fail;
! 967: }
! 968:
! 969: return (ccb);
! 970: fail:
! 971: for (; i > 0; i--)
! 972: bus_dmamap_destroy(sc->sc_dmat, ccb[i - 1].c_dmam);
! 973: free(ccb, M_DEVBUF);
! 974: return (NULL);
! 975: }
! 976:
! 977: void
! 978: ips_ccb_free(struct ips_softc *sc, struct ips_ccb *ccb, int n)
! 979: {
! 980: int i;
! 981:
! 982: for (i = 0; i < n; i++)
! 983: bus_dmamap_destroy(sc->sc_dmat, ccb[i - 1].c_dmam);
! 984: free(ccb, M_DEVBUF);
! 985: }
! 986:
! 987: struct ips_ccb *
! 988: ips_ccb_get(struct ips_softc *sc)
! 989: {
! 990: struct ips_ccb *ccb;
! 991:
! 992: if ((ccb = TAILQ_FIRST(&sc->sc_ccbq_free)) != NULL)
! 993: TAILQ_REMOVE(&sc->sc_ccbq_free, ccb, c_link);
! 994:
! 995: return (ccb);
! 996: }
! 997:
! 998: void
! 999: ips_ccb_put(struct ips_softc *sc, struct ips_ccb *ccb)
! 1000: {
! 1001: ccb->c_flags = 0;
! 1002: ccb->c_xfer = NULL;
! 1003: TAILQ_INSERT_TAIL(&sc->sc_ccbq_free, ccb, c_link);
! 1004: }
! 1005:
! 1006: int
! 1007: ips_dmamem_alloc(struct dmamem *dm, bus_dma_tag_t tag, bus_size_t size)
! 1008: {
! 1009: int nsegs;
! 1010:
! 1011: dm->dm_tag = tag;
! 1012: dm->dm_size = size;
! 1013:
! 1014: if (bus_dmamap_create(tag, size, 1, size, 0,
! 1015: BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &dm->dm_map))
! 1016: return (1);
! 1017: if (bus_dmamem_alloc(tag, size, 0, 0, &dm->dm_seg, 1, &nsegs,
! 1018: BUS_DMA_NOWAIT))
! 1019: goto fail1;
! 1020: if (bus_dmamem_map(tag, &dm->dm_seg, 1, size, (caddr_t *)&dm->dm_vaddr,
! 1021: BUS_DMA_NOWAIT))
! 1022: goto fail2;
! 1023: if (bus_dmamap_load(tag, dm->dm_map, dm->dm_vaddr, size, NULL,
! 1024: BUS_DMA_NOWAIT))
! 1025: goto fail3;
! 1026:
! 1027: return (0);
! 1028:
! 1029: fail3:
! 1030: bus_dmamem_unmap(tag, dm->dm_vaddr, size);
! 1031: fail2:
! 1032: bus_dmamem_free(tag, &dm->dm_seg, 1);
! 1033: fail1:
! 1034: bus_dmamap_destroy(tag, dm->dm_map);
! 1035: return (1);
! 1036: }
! 1037:
! 1038: void
! 1039: ips_dmamem_free(struct dmamem *dm)
! 1040: {
! 1041: bus_dmamap_unload(dm->dm_tag, dm->dm_map);
! 1042: bus_dmamem_unmap(dm->dm_tag, dm->dm_vaddr, dm->dm_size);
! 1043: bus_dmamem_free(dm->dm_tag, &dm->dm_seg, 1);
! 1044: bus_dmamap_destroy(dm->dm_tag, dm->dm_map);
! 1045: }
CVSweb