/* $NetBSD: mesh.c,v 1.13 2001/11/04 12:03:41 tsutsui 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 #include #include #include #ifdef MESH_DEBUG # define DPRINTF printf #else # define DPRINTF while (0) printf #endif #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_READ 0x80 struct mesh_scb { TAILQ_ENTRY(mesh_scb) chain; int flags; struct scsipi_xfer *xs; struct scsi_generic cmd; int cmdlen; int target; /* target SCSI ID */ int resid; vaddr_t daddr; vsize_t dlen; int status; }; /* sc_flags value */ #define MESH_DMA_ACTIVE 0x01 struct mesh_softc { struct device sc_dev; /* us as a device */ struct scsipi_channel sc_channel; struct scsipi_adapter sc_adapter; u_char *sc_reg; /* MESH base address */ dbdma_regmap_t *sc_dmareg; /* DMA register address */ dbdma_command_t *sc_dmacmd; /* DMA command area */ int sc_flags; int sc_cfflags; /* copy of config flags */ int sc_meshid; /* MESH version */ 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]; }; /* mesh_msgout() values */ #define SEND_REJECT 1 #define SEND_IDENTIFY 2 #define SEND_SDTR 4 static __inline int mesh_read_reg __P((struct mesh_softc *, int)); static __inline void mesh_set_reg __P((struct mesh_softc *, int, int)); int mesh_match __P((struct device *, struct cfdata *, void *)); void mesh_attach __P((struct device *, struct device *, void *)); void mesh_shutdownhook __P((void *)); int mesh_intr __P((void *)); void mesh_error __P((struct mesh_softc *, struct mesh_scb *, int, int)); void mesh_select __P((struct mesh_softc *, struct mesh_scb *)); void mesh_identify __P((struct mesh_softc *, struct mesh_scb *)); void mesh_command __P((struct mesh_softc *, struct mesh_scb *)); void mesh_dma_setup __P((struct mesh_softc *, struct mesh_scb *)); void mesh_dataio __P((struct mesh_softc *, struct mesh_scb *)); void mesh_status __P((struct mesh_softc *, struct mesh_scb *)); void mesh_msgin __P((struct mesh_softc *, struct mesh_scb *)); void mesh_msgout __P((struct mesh_softc *, int)); void mesh_bus_reset __P((struct mesh_softc *)); void mesh_reset __P((struct mesh_softc *)); int mesh_stp __P((struct mesh_softc *, int)); void mesh_setsync __P((struct mesh_softc *, struct mesh_tinfo *)); struct mesh_scb *mesh_get_scb __P((struct mesh_softc *)); void mesh_free_scb __P((struct mesh_softc *, struct mesh_scb *)); void mesh_scsi_request __P((struct scsipi_channel *, scsipi_adapter_req_t, void *)); void mesh_sched __P((struct mesh_softc *)); int mesh_poll __P((struct mesh_softc *, struct scsipi_xfer *)); void mesh_done __P((struct mesh_softc *, struct mesh_scb *)); void mesh_timeout __P((void *)); void mesh_minphys __P((struct buf *)); #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) struct cfattach mesh_ca = { sizeof(struct mesh_softc), mesh_match, mesh_attach }; int mesh_match(parent, cf, aux) struct device *parent; struct cfdata *cf; void *aux; { struct confargs *ca = aux; char compat[32]; if (strcmp(ca->ca_name, "mesh") == 0) return 1; memset(compat, 0, sizeof(compat)); OF_getprop(ca->ca_node, "compatible", compat, sizeof(compat)); if (strcmp(compat, "chrp,mesh0") == 0) return 1; return 0; } void mesh_attach(parent, self, aux) struct device *parent, *self; void *aux; { struct mesh_softc *sc = (void *)self; struct confargs *ca = aux; int i; u_int *reg; reg = ca->ca_reg; reg[0] += ca->ca_baseaddr; reg[2] += ca->ca_baseaddr; sc->sc_reg = mapiodev(reg[0], reg[1]); sc->sc_irq = ca->ca_intr[0]; sc->sc_dmareg = mapiodev(reg[2], reg[3]); sc->sc_cfflags = self->dv_cfdata->cf_flags; sc->sc_meshid = mesh_read_reg(sc, MESH_MESH_ID) & 0x1f; #if 0 if (sc->sc_meshid != (MESH_SIGNATURE & 0x1f) { printf(": unknown MESH ID (0x%x)\n", sc->sc_meshid); return; } #endif if (OF_getprop(ca->ca_node, "clock-frequency", &sc->sc_freq, 4) != 4) { printf(": cannot get clock-frequency\n"); return; } 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); 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); sc->sc_dmacmd = dbdma_alloc(sizeof(dbdma_command_t) * 20); mesh_reset(sc); mesh_bus_reset(sc); printf(" irq %d: %dMHz, SCSI ID %d\n", sc->sc_irq, sc->sc_freq, sc->sc_id); sc->sc_adapter.adapt_dev = &sc->sc_dev; sc->sc_adapter.adapt_nchannels = 1; sc->sc_adapter.adapt_openings = 7; sc->sc_adapter.adapt_max_periph = 1; sc->sc_adapter.adapt_ioctl = NULL; sc->sc_adapter.adapt_minphys = mesh_minphys; sc->sc_adapter.adapt_request = mesh_scsi_request; 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 = 8; sc->sc_channel.chan_nluns = 8; sc->sc_channel.chan_id = sc->sc_id; config_found(&sc->sc_dev, &sc->sc_channel, scsiprint); intr_establish(sc->sc_irq, IST_LEVEL, IPL_BIO, mesh_intr, sc); /* Reset SCSI bus when halt. */ shutdownhook_establish(mesh_shutdownhook, sc); } #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(sc, reg) struct mesh_softc *sc; int reg; { return in8(sc->sc_reg + reg); } void mesh_set_reg(sc, reg, val) struct mesh_softc *sc; int reg, val; { out8(sc->sc_reg + reg, val); } void mesh_shutdownhook(arg) void *arg; { struct mesh_softc *sc = arg; /* Set to async mode. */ mesh_set_reg(sc, MESH_SYNC_PARAM, 2); } #ifdef MESH_DEBUG static char scsi_phase[][8] = { "DATAOUT", "DATAIN", "COMMAND", "STATUS", "", "", "MSGOUT", "MSGIN" }; #endif int mesh_intr(arg) 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 { char buf1[64], buf2[64]; bitmask_snprintf(status0, MESH_STATUS0_BITMASK, buf1, sizeof buf1); bitmask_snprintf(exception, MESH_EXC_BITMASK, buf2, sizeof buf2); printf("mesh_intr status0 = 0x%s (%s), exc = 0x%s\n", buf1, scsi_phase[status0 & 7], buf2); } #endif scb = sc->sc_nexus; if (scb == NULL) { DPRINTF("%s: NULL nexus\n", sc->sc_dev.dv_xname); return 1; } if (sc->sc_flags & MESH_DMA_ACTIVE) { dbdma_stop(sc->sc_dmareg); sc->sc_flags &= ~MESH_DMA_ACTIVE; scb->resid = MESH_GET_XFER(sc); fifocnt = mesh_read_reg(sc, MESH_FIFO_COUNT); if (fifocnt != 0 && (scb->flags & MESH_READ)) { char *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); } if (intr & MESH_INTR_ERROR) { 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: mesh_dataio(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(sc, scb, error, exception) struct mesh_softc *sc; struct mesh_scb *scb; int error, 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(sc, scb) struct mesh_softc *sc; struct mesh_scb *scb; { struct mesh_tinfo *ti = &sc->sc_tinfo[scb->target]; int timeout; 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 = (u_int64_t)scb->xs->timeout * (u_int64_t)hz / 1000; if (timeout == 0) timeout = 1; callout_reset(&scb->xs->xs_callout, timeout, mesh_timeout, scb); } void mesh_identify(sc, scb) 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(sc, scb) 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; } void mesh_dma_setup(sc, scb) struct mesh_softc *sc; struct mesh_scb *scb; { int datain = scb->flags & MESH_READ; dbdma_command_t *cmdp; u_int cmd; vaddr_t va; int count, offset; cmdp = sc->sc_dmacmd; cmd = datain ? DBDMA_CMD_IN_MORE : DBDMA_CMD_OUT_MORE; count = scb->dlen; if (count / NBPG > 32) panic("mesh: transfer size >= 128k"); va = scb->daddr; offset = va & PGOFSET; /* if va is not page-aligned, setup the first page */ if (offset != 0) { int rest = NBPG - offset; /* the rest in the page */ if (count > rest) { /* if continues to next page */ DBDMA_BUILD(cmdp, cmd, 0, rest, vtophys(va), DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); count -= rest; va += rest; cmdp++; } } /* now va is page-aligned */ while (count > NBPG) { DBDMA_BUILD(cmdp, cmd, 0, NBPG, vtophys(va), DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); count -= NBPG; va += NBPG; cmdp++; } /* the last page (count <= NBPG here) */ cmd = datain ? DBDMA_CMD_IN_LAST : DBDMA_CMD_OUT_LAST; DBDMA_BUILD(cmdp, cmd , 0, count, vtophys(va), DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); cmdp++; DBDMA_BUILD(cmdp, DBDMA_CMD_STOP, 0, 0, 0, DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); } void mesh_dataio(sc, scb) struct mesh_softc *sc; struct mesh_scb *scb; { DPRINTF("mesh_dataio len = %ld (%s)\n", scb->dlen, scb->flags & MESH_READ ? "read" : "write"); mesh_dma_setup(sc, scb); 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_dmacmd); sc->sc_flags |= MESH_DMA_ACTIVE; sc->sc_nextstate = MESH_STATUS; } void mesh_status(sc, scb) 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(sc, scb) 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 && MSG_IS1BYTE(sc->sc_imsg[0])) goto gotit; if (sc->sc_imsglen == 2 && MSG_IS2BYTE(sc->sc_imsg[0])) goto gotit; if (sc->sc_imsglen >= 3 && MSG_ISEXTENDED(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 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: scsipi_printaddr(scb->xs->xs_periph); 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; } scsipi_printaddr(scb->xs->xs_periph); /* 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(sc, msg) 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->xs_periph->periph_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(sc) 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(sc) 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(sc, v) 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(sc, ti) 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); } struct mesh_scb * mesh_get_scb(sc) struct mesh_softc *sc; { struct mesh_scb *scb; int s; s = splbio(); if ((scb = sc->free_scb.tqh_first) != NULL) TAILQ_REMOVE(&sc->free_scb, scb, chain); splx(s); return scb; } void mesh_free_scb(sc, scb) struct mesh_softc *sc; struct mesh_scb *scb; { int s; s = splbio(); TAILQ_INSERT_HEAD(&sc->free_scb, scb, chain); splx(s); } void mesh_scsi_request(chan, req, arg) struct scsipi_channel *chan; scsipi_adapter_req_t req; void *arg; { struct scsipi_xfer *xs; struct scsipi_periph *periph; struct mesh_softc *sc = (void *)chan->chan_adapter->adapt_dev; struct mesh_scb *scb; u_int flags; int s; switch (req) { case ADAPTER_REQ_RUN_XFER: xs = arg; periph = xs->xs_periph; flags = xs->xs_control; if ((scb = mesh_get_scb(sc)) == NULL) { xs->error = XS_RESOURCE_SHORTAGE; scsipi_done(xs); return; } scb->xs = xs; scb->flags = 0; scb->status = 0; scb->daddr = (vaddr_t)xs->data; scb->dlen = xs->datalen; scb->resid = xs->datalen; memcpy(&scb->cmd, xs->cmd, xs->cmdlen); scb->cmdlen = xs->cmdlen; scb->target = periph->periph_target; sc->sc_imsglen = 0; /* XXX ? */ #ifdef MESH_DEBUG { int i; printf("mesh_scsi_cmd: target = %d, cdb = %02x", scb->target, scb->cmd.opcode); for (i = 0; i < 5; i++) printf(" %02x", scb->cmd.bytes[i]); printf("\n"); } #endif if (flags & XS_CTL_POLL) scb->flags |= MESH_POLL; #if 0 if (flags & XS_CTL_DATA_OUT) scb->flags &= ~MESH_READ; #endif if (flags & XS_CTL_DATA_IN) scb->flags |= MESH_READ; s = splbio(); TAILQ_INSERT_TAIL(&sc->ready_scb, scb, chain); if (sc->sc_nexus == NULL) /* IDLE */ mesh_sched(sc); splx(s); if ((flags & XS_CTL_POLL) == 0) return; if (mesh_poll(sc, xs)) { printf("%s: timeout\n", sc->sc_dev.dv_xname); if (mesh_poll(sc, xs)) printf("%s: timeout again\n", sc->sc_dev.dv_xname); } return; case ADAPTER_REQ_GROW_RESOURCES: /* XXX Not supported. */ return; case ADAPTER_REQ_SET_XFER_MODE: /* XXX Not supported. */ return; } } void mesh_sched(sc) struct mesh_softc *sc; { struct scsipi_xfer *xs; struct mesh_scb *scb; scb = sc->ready_scb.tqh_first; start: if (scb == NULL) return; xs = scb->xs; if (sc->sc_nexus == NULL) { TAILQ_REMOVE(&sc->ready_scb, scb, chain); sc->sc_nexus = scb; mesh_select(sc, scb); return; } scb = scb->chain.tqe_next; goto start; } int mesh_poll(sc, xs) struct mesh_softc *sc; struct scsipi_xfer *xs; { int count = xs->timeout; while (count) { if (mesh_read_reg(sc, MESH_INTERRUPT)) mesh_intr(sc); if (xs->xs_status & XS_STS_DONE) return 0; delay(1000); count--; }; return 1; } void mesh_done(sc, scb) struct mesh_softc *sc; struct mesh_scb *scb; { struct scsipi_xfer *xs = scb->xs; DPRINTF("mesh_done\n"); sc->sc_nextstate = MESH_BUSFREE; sc->sc_nexus = NULL; callout_stop(&scb->xs->xs_callout); if (scb->status == SCSI_BUSY) { xs->error = XS_BUSY; printf("Target busy\n"); } xs->xs_status = scb->status; xs->resid = scb->resid; if (scb->status == SCSI_CHECK) { xs->error = XS_BUSY; } mesh_set_reg(sc, MESH_SYNC_PARAM, 2); if ((xs->xs_control & XS_CTL_POLL) == 0) mesh_sched(sc); scsipi_done(xs); mesh_free_scb(sc, scb); } void mesh_timeout(arg) void *arg; { struct mesh_scb *scb = arg; struct mesh_softc *sc = (void *)scb->xs->xs_periph->periph_channel->chan_adapter->adapt_dev; int s; int status0, status1; int intr, error, exception; printf("%s: timeout state %d\n", sc->sc_dev.dv_xname, sc->sc_nextstate); intr = mesh_read_reg(sc, MESH_INTERRUPT); 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) { printf("mesh: resetting dma\n"); 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(bp) struct buf *bp; { if (bp->b_bcount > 64*1024) bp->b_bcount = 64*1024; minphys(bp); }