/* $OpenBSD: mesh.c,v 1.32 2013/12/06 21:03:05 deraadt Exp $ */ /* $NetBSD: mesh.c,v 1.1 1999/02/19 13:06:03 tsubai Exp $ */ /*- * Copyright (c) 2000 Tsubai Masanari. * Copyright (c) 1999 Internet Research Institute, 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by * Internet Research Institute, Inc. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MESH_DEBUG # define DPRINTF printf #else # define DPRINTF while (0) printf #endif /* MESH register offsets */ #define MESH_XFER_COUNT0 0x00 /* transfer count (low) */ #define MESH_XFER_COUNT1 0x10 /* transfer count (high) */ #define MESH_FIFO 0x20 /* FIFO (16byte depth) */ #define MESH_SEQUENCE 0x30 /* command register */ #define MESH_BUS_STATUS0 0x40 #define MESH_BUS_STATUS1 0x50 #define MESH_FIFO_COUNT 0x60 #define MESH_EXCEPTION 0x70 #define MESH_ERROR 0x80 #define MESH_INTR_MASK 0x90 #define MESH_INTERRUPT 0xa0 #define MESH_SOURCE_ID 0xb0 #define MESH_DEST_ID 0xc0 #define MESH_SYNC_PARAM 0xd0 #define MESH_MESH_ID 0xe0 /* MESH version */ #define MESH_SEL_TIMEOUT 0xf0 /* selection timeout delay */ #define MESH_SIGNATURE 0xe2 /* XXX wrong! */ /* MESH commands */ #define MESH_CMD_ARBITRATE 0x01 #define MESH_CMD_SELECT 0x02 #define MESH_CMD_COMMAND 0x03 #define MESH_CMD_STATUS 0x04 #define MESH_CMD_DATAOUT 0x05 #define MESH_CMD_DATAIN 0x06 #define MESH_CMD_MSGOUT 0x07 #define MESH_CMD_MSGIN 0x08 #define MESH_CMD_BUSFREE 0x09 #define MESH_CMD_ENABLE_PARITY 0x0A #define MESH_CMD_DISABLE_PARITY 0x0B #define MESH_CMD_ENABLE_RESEL 0x0C #define MESH_CMD_DISABLE_RESEL 0x0D #define MESH_CMD_RESET_MESH 0x0E #define MESH_CMD_FLUSH_FIFO 0x0F #define MESH_SEQ_DMA 0x80 #define MESH_SEQ_TARGET 0x40 #define MESH_SEQ_ATN 0x20 #define MESH_SEQ_ACTNEG 0x10 /* INTERRUPT/INTR_MASK register bits */ #define MESH_INTR_ERROR 0x04 #define MESH_INTR_EXCEPTION 0x02 #define MESH_INTR_CMDDONE 0x01 /* EXCEPTION register bits */ #define MESH_EXC_SELATN 0x20 /* selected and ATN asserted (T) */ #define MESH_EXC_SELECTED 0x10 /* selected (T) */ #define MESH_EXC_RESEL 0x08 /* reselected */ #define MESH_EXC_ARBLOST 0x04 /* arbitration lost */ #define MESH_EXC_PHASEMM 0x02 /* phase mismatch */ #define MESH_EXC_SELTO 0x01 /* selection timeout */ /* ERROR register bits */ #define MESH_ERR_DISCONNECT 0x40 /* unexpected disconnect */ #define MESH_ERR_SCSI_RESET 0x20 /* Rst signal asserted */ #define MESH_ERR_SEQERR 0x10 /* sequence error */ #define MESH_ERR_PARITY_ERR3 0x08 /* parity error */ #define MESH_ERR_PARITY_ERR2 0x04 #define MESH_ERR_PARITY_ERR1 0x02 #define MESH_ERR_PARITY_ERR0 0x01 /* BUS_STATUS0 status bits */ #define MESH_STATUS0_REQ32 0x80 #define MESH_STATUS0_ACK32 0x40 #define MESH_STATUS0_REQ 0x20 #define MESH_STATUS0_ACK 0x10 #define MESH_STATUS0_ATN 0x08 #define MESH_STATUS0_MSG 0x04 #define MESH_STATUS0_CD 0x02 #define MESH_STATUS0_IO 0x01 /* BUS_STATUS1 status bits */ #define MESH_STATUS1_RST 0x80 #define MESH_STATUS1_BSY 0x40 #define MESH_STATUS1_SEL 0x20 #define T_SYNCMODE 0x01 /* target uses sync mode */ #define T_SYNCNEGO 0x02 /* sync negotiation done */ struct mesh_tinfo { int flags; int period; int offset; }; /* scb flags */ #define MESH_POLL 0x01 #define MESH_CHECK 0x02 #define MESH_SENSE 0x04 #define MESH_READ 0x80 struct mesh_scb { TAILQ_ENTRY(mesh_scb) chain; int flags; struct scsi_xfer *xs; struct scsi_generic cmd; int cmdlen; int target; /* target SCSI ID */ int resid; void *daddr; vsize_t dlen; int status; }; /* sc_flags value */ #define MESH_DMA_ACTIVE 0x01 #define MESH_DMALIST_MAX 32 struct mesh_softc { struct device sc_dev; /* us as a device */ struct scsi_link sc_link; u_char *sc_reg; /* MESH base address */ bus_dmamap_t sc_dmamap; bus_dma_tag_t sc_dmat; struct dbdma_regmap *sc_dmareg; /* DMA register address */ struct dbdma_command *sc_dmacmd; /* DMA command area */ dbdma_t sc_dbdma; int sc_flags; int sc_cfflags; /* copy of config flags */ int sc_minsync; /* minimum sync period */ int sc_irq; int sc_freq; /* SCSI bus frequency in MHz */ int sc_id; /* our SCSI ID */ struct mesh_tinfo sc_tinfo[8]; /* target information */ int sc_nextstate; int sc_prevphase; struct mesh_scb *sc_nexus; /* current command */ int sc_msgout; int sc_imsglen; u_char sc_imsg[16]; u_char sc_omsg[16]; TAILQ_HEAD(, mesh_scb) free_scb; TAILQ_HEAD(, mesh_scb) ready_scb; struct mesh_scb sc_scb[16]; struct timeout sc_tmo; struct mutex sc_scb_mtx; struct scsi_iopool sc_iopool; }; /* mesh_msgout() values */ #define SEND_REJECT 1 #define SEND_IDENTIFY 2 #define SEND_SDTR 4 static inline int mesh_read_reg(struct mesh_softc *, int); static inline void mesh_set_reg(struct mesh_softc *, int, int); int mesh_match(struct device *, void *, void *); void mesh_attach(struct device *, struct device *, void *); int mesh_activate(struct device *, int); int mesh_intr(void *); void mesh_error(struct mesh_softc *, struct mesh_scb *, int, int); void mesh_select(struct mesh_softc *, struct mesh_scb *); void mesh_identify(struct mesh_softc *, struct mesh_scb *); void mesh_command(struct mesh_softc *, struct mesh_scb *); int mesh_dma_setup(struct mesh_softc *, struct mesh_scb *); int mesh_dataio(struct mesh_softc *, struct mesh_scb *); void mesh_status(struct mesh_softc *, struct mesh_scb *); void mesh_msgin(struct mesh_softc *, struct mesh_scb *); void mesh_msgout(struct mesh_softc *, int); void mesh_bus_reset(struct mesh_softc *); void mesh_reset(struct mesh_softc *); int mesh_stp(struct mesh_softc *, int); void mesh_setsync(struct mesh_softc *, struct mesh_tinfo *); struct mesh_scb *mesh_get_scb(struct mesh_softc *); void *mesh_scb_alloc(void *); void mesh_scb_free(void *, void *); void mesh_scsi_cmd(struct scsi_xfer *); void mesh_sched(struct mesh_softc *); int mesh_poll(struct scsi_xfer *); void mesh_done(struct mesh_softc *, struct mesh_scb *); void mesh_timeout(void *); void mesh_minphys(struct buf *, struct scsi_link *); struct cfattach mesh_ca = { sizeof(struct mesh_softc), mesh_match, mesh_attach, NULL, mesh_activate }; struct cfdriver mesh_cd = { NULL, "mesh", DV_DULL }; struct scsi_adapter mesh_switch = { mesh_scsi_cmd, mesh_minphys, NULL, NULL }; #define MESH_DATAOUT 0 #define MESH_DATAIN MESH_STATUS0_IO #define MESH_COMMAND MESH_STATUS0_CD #define MESH_STATUS (MESH_STATUS0_CD | MESH_STATUS0_IO) #define MESH_MSGOUT (MESH_STATUS0_MSG | MESH_STATUS0_CD) #define MESH_MSGIN (MESH_STATUS0_MSG | MESH_STATUS0_CD | MESH_STATUS0_IO) #define MESH_SELECTING 8 #define MESH_IDENTIFY 9 #define MESH_COMPLETE 10 #define MESH_BUSFREE 11 #define MESH_UNKNOWN -1 #define MESH_PHASE_MASK (MESH_STATUS0_MSG | MESH_STATUS0_CD | MESH_STATUS0_IO) int mesh_match(struct device *parent, void *vcf, void *aux) { struct confargs *ca = aux; char compat[32]; if (strcmp(ca->ca_name, "mesh") == 0) return 1; bzero(compat, sizeof(compat)); OF_getprop(ca->ca_node, "compatible", compat, sizeof(compat)); if (strcmp(compat, "chrp,mesh0") == 0) return 1; return 0; } void mesh_attach(struct device *parent, struct device *self, void *aux) { struct mesh_softc *sc = (void *)self; struct confargs *ca = aux; struct scsibus_attach_args saa; int i, error; u_int *reg; reg = ca->ca_reg; reg[0] += ca->ca_baseaddr; reg[2] += ca->ca_baseaddr; if ((sc->sc_reg = mapiodev(reg[0], reg[1])) == NULL) { printf(": cannot map device registers\n"); return; } sc->sc_irq = ca->ca_intr[0]; if ((sc->sc_dmareg = mapiodev(reg[2], reg[3])) == NULL) { printf(": cannot map DMA registers\n"); goto noreg; } sc->sc_cfflags = sc->sc_dev.dv_cfdata->cf_flags; if (OF_getprop(ca->ca_node, "clock-frequency", &sc->sc_freq, 4) != 4) { printf(": cannot get clock-frequency\n"); goto nofreq; } sc->sc_dmat = ca->ca_dmat; if ((error = bus_dmamap_create(sc->sc_dmat, MESH_DMALIST_MAX * DBDMA_COUNT_MAX, MESH_DMALIST_MAX, DBDMA_COUNT_MAX, NBPG, BUS_DMA_NOWAIT, &sc->sc_dmamap)) != 0) { printf(": cannot create DMA map, error = %d\n", error); goto nofreq; } sc->sc_freq /= 1000000; /* in MHz */ sc->sc_minsync = 25; /* maximum sync rate = 10MB/sec */ sc->sc_id = 7; TAILQ_INIT(&sc->free_scb); TAILQ_INIT(&sc->ready_scb); mtx_init(&sc->sc_scb_mtx, IPL_BIO); scsi_iopool_init(&sc->sc_iopool, sc, mesh_scb_alloc, mesh_scb_free); for (i = 0; i < sizeof(sc->sc_scb)/sizeof(sc->sc_scb[0]); i++) TAILQ_INSERT_TAIL(&sc->free_scb, &sc->sc_scb[i], chain); if ((sc->sc_dbdma = dbdma_alloc(sc->sc_dmat, MESH_DMALIST_MAX)) == NULL) { printf(": cannot alloc dma descriptors\n"); goto nodbdma; } sc->sc_dmacmd = sc->sc_dbdma->d_addr; timeout_set(&sc->sc_tmo, mesh_timeout, sc); mesh_reset(sc); mesh_bus_reset(sc); printf(" irq %d: %dMHz\n", sc->sc_irq, sc->sc_freq); sc->sc_link.adapter_softc = sc; sc->sc_link.adapter_target = sc->sc_id; sc->sc_link.adapter = &mesh_switch; sc->sc_link.openings = 2; sc->sc_link.pool = &sc->sc_iopool; bzero(&saa, sizeof(saa)); saa.saa_sc_link = &sc->sc_link; config_found(&sc->sc_dev, &saa, scsiprint); mac_intr_establish(parent, sc->sc_irq, IST_LEVEL, IPL_BIO, mesh_intr, sc, sc->sc_dev.dv_xname); return; nodbdma: bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap); nofreq: unmapiodev(sc->sc_dmareg, reg[3]); noreg: unmapiodev(sc->sc_reg, reg[1]); } int mesh_activate(struct device *self, int act) { struct mesh_softc *sc = (struct mesh_softc *)self; int rv = 0; switch (act) { case DVACT_POWERDOWN: rv = config_activate_children(self, act); /* Set to async mode. */ mesh_set_reg(sc, MESH_SYNC_PARAM, 2); mesh_bus_reset(sc); break; default: rv = config_activate_children(self, act); break; } return (rv); } #define MESH_SET_XFER(sc, count) do { \ mesh_set_reg(sc, MESH_XFER_COUNT0, count); \ mesh_set_reg(sc, MESH_XFER_COUNT1, count >> 8); \ } while (0) #define MESH_GET_XFER(sc) ((mesh_read_reg(sc, MESH_XFER_COUNT1) << 8) | \ mesh_read_reg(sc, MESH_XFER_COUNT0)) int mesh_read_reg(struct mesh_softc *sc, int reg) { return in8(sc->sc_reg + reg); } void mesh_set_reg(struct mesh_softc *sc, int reg, int val) { out8(sc->sc_reg + reg, val); } #ifdef MESH_DEBUG static char scsi_phase[][8] = { "DATAOUT", "DATAIN", "COMMAND", "STATUS", "", "", "MSGOUT", "MSGIN" }; #endif int mesh_intr(void *arg) { struct mesh_softc *sc = arg; struct mesh_scb *scb; int fifocnt; u_char intr, exception, error, status0, status1; intr = mesh_read_reg(sc, MESH_INTERRUPT); if (intr == 0) { DPRINTF("%s: stray interrupt\n", sc->sc_dev.dv_xname); return 0; } exception = mesh_read_reg(sc, MESH_EXCEPTION); error = mesh_read_reg(sc, MESH_ERROR); status0 = mesh_read_reg(sc, MESH_BUS_STATUS0); status1 = mesh_read_reg(sc, MESH_BUS_STATUS1); /* clear interrupt */ mesh_set_reg(sc, MESH_INTERRUPT, intr); #ifdef MESH_DEBUG { printf("mesh_intr status0 = 0x%x (%s), exc = 0x%x\n", status0, scsi_phase[status0 & 7], exception); } #endif scb = sc->sc_nexus; if (scb == NULL) { DPRINTF("%s: NULL nexus\n", sc->sc_dev.dv_xname); return 1; } if (intr & MESH_INTR_CMDDONE) { if (sc->sc_flags & MESH_DMA_ACTIVE) { dbdma_stop(sc->sc_dmareg); bus_dmamap_unload(sc->sc_dmat, sc->sc_dmamap); sc->sc_flags &= ~MESH_DMA_ACTIVE; scb->resid = MESH_GET_XFER(sc); fifocnt = mesh_read_reg(sc, MESH_FIFO_COUNT); if (fifocnt != 0) { if (scb->flags & MESH_READ) { char *cp; cp = (char *)scb->daddr + scb->dlen - fifocnt; DPRINTF("fifocnt = %d, resid = %d\n", fifocnt, scb->resid); while (fifocnt > 0) { *cp++ = mesh_read_reg(sc, MESH_FIFO); fifocnt--; } } else { mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_FLUSH_FIFO); } } else { /* Clear all interrupts */ mesh_set_reg(sc, MESH_INTERRUPT, 7); } } } if (intr & MESH_INTR_ERROR) { printf("%s: error %02x %02x\n", sc->sc_dev.dv_xname, error, exception); mesh_error(sc, scb, error, 0); return 1; } if (intr & MESH_INTR_EXCEPTION) { /* selection timeout */ if (exception & MESH_EXC_SELTO) { mesh_error(sc, scb, 0, exception); return 1; } /* phase mismatch */ if (exception & MESH_EXC_PHASEMM) { DPRINTF("%s: PHASE MISMATCH; nextstate = %d -> ", sc->sc_dev.dv_xname, sc->sc_nextstate); sc->sc_nextstate = status0 & MESH_PHASE_MASK; DPRINTF("%d, resid = %d\n", sc->sc_nextstate, scb->resid); } } if (sc->sc_nextstate == MESH_UNKNOWN) sc->sc_nextstate = status0 & MESH_PHASE_MASK; switch (sc->sc_nextstate) { case MESH_IDENTIFY: mesh_identify(sc, scb); break; case MESH_COMMAND: mesh_command(sc, scb); break; case MESH_DATAIN: case MESH_DATAOUT: if (mesh_dataio(sc, scb)) { scb->xs->error = XS_DRIVER_STUFFUP; mesh_done(sc, scb); } break; case MESH_STATUS: mesh_status(sc, scb); break; case MESH_MSGIN: mesh_msgin(sc, scb); break; case MESH_COMPLETE: mesh_done(sc, scb); break; default: printf("%s: unknown state (%d)\n", sc->sc_dev.dv_xname, sc->sc_nextstate); scb->xs->error = XS_DRIVER_STUFFUP; mesh_done(sc, scb); } return 1; } void mesh_error(struct mesh_softc *sc, struct mesh_scb *scb, int error, int exception) { if (error & MESH_ERR_SCSI_RESET) { printf("%s: SCSI RESET\n", sc->sc_dev.dv_xname); /* Wait until the RST signal is deasserted. */ while (mesh_read_reg(sc, MESH_BUS_STATUS1) & MESH_STATUS1_RST); mesh_reset(sc); return; } if (error & MESH_ERR_PARITY_ERR0) { printf("%s: parity error\n", sc->sc_dev.dv_xname); scb->xs->error = XS_DRIVER_STUFFUP; } if (error & MESH_ERR_DISCONNECT) { printf("%s: unexpected disconnect\n", sc->sc_dev.dv_xname); if (sc->sc_nextstate != MESH_COMPLETE) scb->xs->error = XS_DRIVER_STUFFUP; } if (exception & MESH_EXC_SELTO) { /* XXX should reset bus here? */ scb->xs->error = XS_SELTIMEOUT; } mesh_done(sc, scb); } void mesh_select(struct mesh_softc *sc, struct mesh_scb *scb) { struct mesh_tinfo *ti = &sc->sc_tinfo[scb->target]; DPRINTF("mesh_select\n"); mesh_setsync(sc, ti); MESH_SET_XFER(sc, 0); /* arbitration */ /* * MESH mistakenly asserts TARGET ID bit along with its own ID bit * in arbitration phase (like selection). So we should load * initiator ID to DestID register temporarily. */ mesh_set_reg(sc, MESH_DEST_ID, sc->sc_id); mesh_set_reg(sc, MESH_INTR_MASK, 0); /* disable intr. */ mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_ARBITRATE); while (mesh_read_reg(sc, MESH_INTERRUPT) == 0); mesh_set_reg(sc, MESH_INTERRUPT, 1); mesh_set_reg(sc, MESH_INTR_MASK, 7); /* selection */ mesh_set_reg(sc, MESH_DEST_ID, scb->target); mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_SELECT | MESH_SEQ_ATN); sc->sc_prevphase = MESH_SELECTING; sc->sc_nextstate = MESH_IDENTIFY; timeout_add_sec(&sc->sc_tmo, 10); } void mesh_identify(struct mesh_softc *sc, struct mesh_scb *scb) { struct mesh_tinfo *ti = &sc->sc_tinfo[scb->target]; DPRINTF("mesh_identify\n"); mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_FLUSH_FIFO); if ((ti->flags & T_SYNCNEGO) == 0) { ti->period = sc->sc_minsync; ti->offset = 15; mesh_msgout(sc, SEND_IDENTIFY | SEND_SDTR); sc->sc_nextstate = MESH_MSGIN; } else { mesh_msgout(sc, SEND_IDENTIFY); sc->sc_nextstate = MESH_COMMAND; } } void mesh_command(struct mesh_softc *sc, struct mesh_scb *scb) { int i; char *cmdp; #ifdef MESH_DEBUG printf("mesh_command cdb = %02x", scb->cmd.opcode); for (i = 0; i < 5; i++) printf(" %02x", scb->cmd.bytes[i]); printf("\n"); #endif mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_FLUSH_FIFO); MESH_SET_XFER(sc, scb->cmdlen); mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_COMMAND); cmdp = (char *)&scb->cmd; for (i = 0; i < scb->cmdlen; i++) mesh_set_reg(sc, MESH_FIFO, *cmdp++); if (scb->resid == 0) sc->sc_nextstate = MESH_STATUS; /* no data xfer */ else sc->sc_nextstate = MESH_DATAIN; } int mesh_dma_setup(struct mesh_softc *sc, struct mesh_scb *scb) { int datain = scb->flags & MESH_READ; struct dbdma_command *cmdp; u_int cmd; int i, error; if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, scb->daddr, scb->dlen, NULL, BUS_DMA_NOWAIT)) != 0) return (error); cmdp = sc->sc_dmacmd; cmd = datain ? DBDMA_CMD_IN_MORE : DBDMA_CMD_OUT_MORE; for (i = 0; i < sc->sc_dmamap->dm_nsegs; i++, cmdp++) { if (i + 1 == sc->sc_dmamap->dm_nsegs) cmd = datain ? DBDMA_CMD_IN_LAST : DBDMA_CMD_OUT_LAST; DBDMA_BUILD(cmdp, cmd, 0, sc->sc_dmamap->dm_segs[i].ds_len, sc->sc_dmamap->dm_segs[i].ds_addr, DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); } DBDMA_BUILD(cmdp, DBDMA_CMD_STOP, 0, 0, 0, DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); return(0); } int mesh_dataio(struct mesh_softc *sc, struct mesh_scb *scb) { int error; if ((error = mesh_dma_setup(sc, scb))) return(error); if (scb->dlen == 65536) MESH_SET_XFER(sc, 0); /* TC = 0 means 64KB transfer */ else MESH_SET_XFER(sc, scb->dlen); if (scb->flags & MESH_READ) mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_DATAIN | MESH_SEQ_DMA); else mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_DATAOUT | MESH_SEQ_DMA); dbdma_start(sc->sc_dmareg, sc->sc_dbdma); sc->sc_flags |= MESH_DMA_ACTIVE; sc->sc_nextstate = MESH_STATUS; return(0); } void mesh_status(struct mesh_softc *sc, struct mesh_scb *scb) { if (mesh_read_reg(sc, MESH_FIFO_COUNT) == 0) { /* XXX cheat */ DPRINTF("mesh_status(0)\n"); MESH_SET_XFER(sc, 1); mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_STATUS); sc->sc_nextstate = MESH_STATUS; return; } scb->status = mesh_read_reg(sc, MESH_FIFO); DPRINTF("mesh_status(1): status = 0x%x\n", scb->status); if (mesh_read_reg(sc, MESH_FIFO_COUNT) != 0) DPRINTF("FIFO_COUNT=%d\n", mesh_read_reg(sc, MESH_FIFO_COUNT)); mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_FLUSH_FIFO); MESH_SET_XFER(sc, 1); mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_MSGIN); sc->sc_nextstate = MESH_MSGIN; } void mesh_msgin(struct mesh_softc *sc, struct mesh_scb *scb) { DPRINTF("mesh_msgin\n"); if (mesh_read_reg(sc, MESH_FIFO_COUNT) == 0) { /* XXX cheat */ MESH_SET_XFER(sc, 1); mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_MSGIN); sc->sc_imsglen = 0; sc->sc_nextstate = MESH_MSGIN; return; } sc->sc_imsg[sc->sc_imsglen++] = mesh_read_reg(sc, MESH_FIFO); if (sc->sc_imsglen == 1 && IS1BYTEMSG(sc->sc_imsg[0])) goto gotit; if (sc->sc_imsglen == 2 && IS2BYTEMSG(sc->sc_imsg[0])) goto gotit; if (sc->sc_imsglen >= 3 && ISEXTMSG(sc->sc_imsg[0]) && sc->sc_imsglen == sc->sc_imsg[1] + 2) goto gotit; sc->sc_nextstate = MESH_MSGIN; MESH_SET_XFER(sc, 1); mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_MSGIN); return; gotit: #ifdef MESH_DEBUG { int i; printf("msgin:"); for (i = 0; i < sc->sc_imsglen; i++) printf(" 0x%02x", sc->sc_imsg[i]); printf("\n"); } #endif switch (sc->sc_imsg[0]) { case MSG_CMDCOMPLETE: mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_BUSFREE); sc->sc_nextstate = MESH_COMPLETE; sc->sc_imsglen = 0; return; case MSG_MESSAGE_REJECT: if (sc->sc_msgout & SEND_SDTR) { printf("SDTR rejected\n"); printf("using async mode\n"); sc->sc_tinfo[scb->target].period = 0; sc->sc_tinfo[scb->target].offset = 0; mesh_setsync(sc, &sc->sc_tinfo[scb->target]); break; } break; case MSG_NOOP: break; case MSG_EXTENDED: goto extended_msg; default: sc_print_addr(scb->xs->sc_link); printf("unrecognized MESSAGE(0x%02x); sending REJECT\n", sc->sc_imsg[0]); reject: mesh_msgout(sc, SEND_REJECT); return; } goto done; extended_msg: /* process an extended message */ switch (sc->sc_imsg[2]) { case MSG_EXT_SDTR: { struct mesh_tinfo *ti = &sc->sc_tinfo[scb->target]; int period = sc->sc_imsg[3]; int offset = sc->sc_imsg[4]; int r = 250 / period; int s = (100*250) / period - 100 * r; if (period < sc->sc_minsync) { ti->period = sc->sc_minsync; ti->offset = 15; mesh_msgout(sc, SEND_SDTR); return; } sc_print_addr(scb->xs->sc_link); /* XXX if (offset != 0) ... */ printf("max sync rate %d.%02dMb/s\n", r, s); ti->period = period; ti->offset = offset; ti->flags |= T_SYNCNEGO; ti->flags |= T_SYNCMODE; mesh_setsync(sc, ti); goto done; } default: printf("%s target %d: rejecting extended message 0x%x\n", sc->sc_dev.dv_xname, scb->target, sc->sc_imsg[0]); goto reject; } done: sc->sc_imsglen = 0; sc->sc_nextstate = MESH_UNKNOWN; mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_BUSFREE); /* XXX really? */ } void mesh_msgout(struct mesh_softc *sc, int msg) { struct mesh_scb *scb = sc->sc_nexus; struct mesh_tinfo *ti; int lun, len, i; DPRINTF("mesh_msgout: sending"); sc->sc_msgout = msg; len = 0; if (msg & SEND_REJECT) { DPRINTF(" REJECT"); sc->sc_omsg[len++] = MSG_MESSAGE_REJECT; } if (msg & SEND_IDENTIFY) { DPRINTF(" IDENTIFY"); lun = scb->xs->sc_link->lun; sc->sc_omsg[len++] = MSG_IDENTIFY(lun, 0); } if (msg & SEND_SDTR) { DPRINTF(" SDTR"); ti = &sc->sc_tinfo[scb->target]; sc->sc_omsg[len++] = MSG_EXTENDED; sc->sc_omsg[len++] = 3; sc->sc_omsg[len++] = MSG_EXT_SDTR; sc->sc_omsg[len++] = ti->period; sc->sc_omsg[len++] = ti->offset; } DPRINTF("\n"); MESH_SET_XFER(sc, len); if (len == 1) { mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_MSGOUT); mesh_set_reg(sc, MESH_FIFO, sc->sc_omsg[0]); } else { mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_MSGOUT | MESH_SEQ_ATN); for (i = 0; i < len - 1; i++) mesh_set_reg(sc, MESH_FIFO, sc->sc_omsg[i]); /* Wait for the FIFO empty... */ while (mesh_read_reg(sc, MESH_FIFO_COUNT) > 0); /* ...then write the last byte. */ mesh_set_reg(sc, MESH_FIFO, sc->sc_omsg[i]); } sc->sc_nextstate = MESH_UNKNOWN; } void mesh_bus_reset(struct mesh_softc *sc) { DPRINTF("mesh_bus_reset\n"); /* Disable interrupts. */ mesh_set_reg(sc, MESH_INTR_MASK, 0); /* Assert RST line. */ mesh_set_reg(sc, MESH_BUS_STATUS1, MESH_STATUS1_RST); delay(50); mesh_set_reg(sc, MESH_BUS_STATUS1, 0); mesh_reset(sc); } void mesh_reset(struct mesh_softc *sc) { int i; DPRINTF("mesh_reset\n"); /* Reset DMA first. */ dbdma_reset(sc->sc_dmareg); /* Disable interrupts. */ mesh_set_reg(sc, MESH_INTR_MASK, 0); mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_RESET_MESH); delay(1); /* Wait for reset done. */ while (mesh_read_reg(sc, MESH_INTERRUPT) == 0); /* Clear interrupts */ mesh_set_reg(sc, MESH_INTERRUPT, 0x7); /* Set SCSI ID */ mesh_set_reg(sc, MESH_SOURCE_ID, sc->sc_id); /* Set to async mode by default. */ mesh_set_reg(sc, MESH_SYNC_PARAM, 2); /* Set selection timeout to 250ms. */ mesh_set_reg(sc, MESH_SEL_TIMEOUT, 250 * sc->sc_freq / 500); /* Enable parity check. */ mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_ENABLE_PARITY); /* Enable all interrupts. */ mesh_set_reg(sc, MESH_INTR_MASK, 0x7); for (i = 0; i < 7; i++) { struct mesh_tinfo *ti = &sc->sc_tinfo[i]; ti->flags = 0; ti->period = ti->offset = 0; if (sc->sc_cfflags & (0x100 << i)) ti->flags |= T_SYNCNEGO; } sc->sc_nexus = NULL; } int mesh_stp(struct mesh_softc *sc, int v) { /* * stp(v) = 5 * clock_period (v == 0) * = (v + 2) * 2 clock_period (v > 0) */ if (v == 0) return 5 * 250 / sc->sc_freq; else return (v + 2) * 2 * 250 / sc->sc_freq; } void mesh_setsync(struct mesh_softc *sc, struct mesh_tinfo *ti) { int period = ti->period; int offset = ti->offset; int v; if ((ti->flags & T_SYNCMODE) == 0) offset = 0; if (offset == 0) { /* async mode */ mesh_set_reg(sc, MESH_SYNC_PARAM, 2); return; } v = period * sc->sc_freq / 250 / 2 - 2; if (v < 0) v = 0; if (mesh_stp(sc, v) < period) v++; if (v > 15) v = 15; mesh_set_reg(sc, MESH_SYNC_PARAM, (offset << 4) | v); } void * mesh_scb_alloc(void *xsc) { struct mesh_softc *sc = xsc; struct mesh_scb *scb; mtx_enter(&sc->sc_scb_mtx); scb = TAILQ_FIRST(&sc->free_scb); if (scb) TAILQ_REMOVE(&sc->free_scb, scb, chain); mtx_leave(&sc->sc_scb_mtx); return scb; } void mesh_scb_free(void *xsc, void *xscb) { struct mesh_softc *sc = xsc; struct mesh_scb *scb = xscb; mtx_enter(&sc->sc_scb_mtx); TAILQ_INSERT_TAIL(&sc->free_scb, scb, chain); mtx_leave(&sc->sc_scb_mtx); } void mesh_scsi_cmd(struct scsi_xfer *xs) { struct scsi_link *sc_link = xs->sc_link;; struct mesh_softc *sc = sc_link->adapter_softc; struct mesh_scb *scb; u_int flags; int s; flags = xs->flags; scb = xs->io; DPRINTF("cmdlen: %d\n", xs->cmdlen); scb->xs = xs; scb->flags = 0; scb->status = 0; scb->daddr = xs->data; scb->dlen = xs->datalen; scb->resid = xs->datalen; bcopy(xs->cmd, &scb->cmd, xs->cmdlen); scb->cmdlen = xs->cmdlen; scb->target = sc_link->target; sc->sc_imsglen = 0; /* XXX ? */ if (flags & SCSI_POLL) scb->flags |= MESH_POLL; if (flags & SCSI_DATA_IN) scb->flags |= MESH_READ; s = splbio(); TAILQ_INSERT_TAIL(&sc->ready_scb, scb, chain); if (sc->sc_nexus == NULL) mesh_sched(sc); splx(s); if (xs->flags & SCSI_POLL) { if (mesh_poll(xs)) { printf("%s: poll timeout\n", sc->sc_dev.dv_xname); } } } void mesh_sched(struct mesh_softc *sc) { struct mesh_scb *scb; TAILQ_FOREACH(scb, &sc->ready_scb, chain) { if (sc->sc_nexus == NULL) { TAILQ_REMOVE(&sc->ready_scb, scb, chain); sc->sc_nexus = scb; mesh_select(sc, scb); return; } } } int mesh_poll(struct scsi_xfer *xs) { struct scsi_link *link = xs->sc_link; struct mesh_softc *sc = link->adapter_softc; int count = xs->timeout; while (count) { if (mesh_read_reg(sc, MESH_INTERRUPT)) mesh_intr(sc); if (xs->flags & ITSDONE) return 0; DELAY(1000); count--; } return 1; } void mesh_done(struct mesh_softc *sc, struct mesh_scb *scb) { struct scsi_xfer *xs = scb->xs; DPRINTF("mesh_done\n"); sc->sc_nextstate = MESH_BUSFREE; sc->sc_nexus = NULL; timeout_del(&sc->sc_tmo); if (scb->status == SCSI_BUSY) { xs->error = XS_BUSY; printf("Target busy\n"); } if (scb->status == SCSI_CHECK) xs->error = XS_BUSY; xs->status = scb->status; xs->resid = scb->resid; mesh_set_reg(sc, MESH_SYNC_PARAM, 2); if ((xs->flags & SCSI_POLL) == 0) mesh_sched(sc); scsi_done(xs); } void mesh_timeout(void *arg) { struct mesh_softc *sc = arg; struct mesh_scb *scb = sc->sc_nexus; int s; int status0, status1; int intr, error, exception, imsk; printf("%s: timeout state %d\n", sc->sc_dev.dv_xname, sc->sc_nextstate); intr = mesh_read_reg(sc, MESH_INTERRUPT); imsk = mesh_read_reg(sc, MESH_INTR_MASK); exception = mesh_read_reg(sc, MESH_EXCEPTION); error = mesh_read_reg(sc, MESH_ERROR); status0 = mesh_read_reg(sc, MESH_BUS_STATUS0); status1 = mesh_read_reg(sc, MESH_BUS_STATUS1); s = splbio(); if (sc->sc_flags & MESH_DMA_ACTIVE) { dbdma_reset(sc->sc_dmareg); } scb->xs->error = XS_TIMEOUT; mesh_set_reg(sc, MESH_SEQUENCE, MESH_CMD_BUSFREE); sc->sc_nextstate = MESH_COMPLETE; splx(s); } void mesh_minphys(struct buf *bp, struct scsi_link *sl) { if (bp->b_bcount > 64*1024) bp->b_bcount = 64*1024; minphys(bp); }