📄 esp.c
字号:
/* * Copyright (c) 1988, 1992, 1993 * The Regents of the University of California. All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Lawrence Berkeley Laboratory. * * 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)esp.c 8.2 (Berkeley) 12/14/93 * * from: $Header: esp.c,v 1.28 93/04/27 14:40:44 torek Exp $ (LBL) * * Loosely derived from Mary Baker's devSCSIC90.c from the Berkeley * Sprite project, which is: * * Copyright 1988 Regents of the University of California * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without * fee is hereby granted, provided that the above copyright * notice appear in all copies. The University of California * makes no representations about the suitability of this * software for any purpose. It is provided "as is" without * express or implied warranty. * * from /sprite/src/kernel/dev/sun4c.md/RCS/devSCSIC90.c,v 1.4 * 90/12/19 12:37:58 mgbaker Exp $ SPRITE (Berkeley) *//* * Sbus ESP/DMA driver. A single driver must be used for both devices * as they are physically tied to each other: The DMA chip can only * be used to assist ESP SCSI transactions; the ESP interrupt enable is * in the DMA chip csr. * * Since DMA and SCSI interrupts are handled in the same routine, the * DMA device does not declare itself as an sbus device. This saves * some space. */#include <sys/param.h>#include <sys/buf.h>#include <sys/device.h>#include <sys/malloc.h>#include <dev/scsi/scsi.h>#include <dev/scsi/scsivar.h>#include <machine/autoconf.h>#include <machine/cpu.h>#include <sparc/sbus/dmareg.h>#define ESP_PHASE_NAMES#include <sparc/sbus/espreg.h>#include <sparc/sbus/sbusvar.h>#include <libkern/libkern.h>/* * This driver is largely a giant state machine: * * Given some previous SCSI state (as set up or tracked by us * earlier) and the interrupt registers provided on the chips * (dmacsr, espstat, espstep, and espintr), derive an action. * In many cases this is just a matter of reading the target's * phase and following its orders, which sets a new state. * * This sequencing is done in espact(); the state is primed in espselect(). * * Data transfer is always done via DMA. Unfortunately, there are * limits in the DMA and ESP chips on how much data can be moved * in a single operation. The ESP chip has a 16-bit counter, so * it is limited to 65536 bytes. More insidiously, while the DMA * chip has a 32-bit address, this is composed of a 24-bit counter * with an 8-bit latch, so it cannot cross a 16 MB boundary. To * handle these, we program a smaller count than our caller requests; * when this shorter transfer is done, if the target is still up * for data transfer, we simply keep going (updating the DMA address) * as needed. * * Another state bit is used to recover from bus resets: * * A single TEST UNIT READY is attempted on each target before any * real communication begins; this TEST UNIT READY is allowed to * fail in any way. This is required for the Quantum ProDrive 100 * MB disks, for instance, which respond to their first selection * with status phase, and for anything that insists on implementing * the broken SCSI-2 synch transfer initial message. * * This is done in espclear() (which calls espselect(); functions that * call espselect() must check for clearing first). * * The state machines actually intermingle, as some SCSI sequences are * only allowed during clearing. *//* per-DMA variables */struct dma_softc { struct device dc_dev; /* base device */ volatile struct dmareg *dc_dma; /* register virtual address */ int dc_dmarev; /* revision */ char *dc_dmafmt; /* format for error messages */};void dmaattach(struct device *, struct device *, void *);struct cfdriver dmacd = { NULL, "dma", matchbyname, dmaattach, DV_DULL, sizeof(struct dma_softc) };/* per-ESP variables */struct esp_softc { /* * External interfaces. */ struct hba_softc sc_hba; /* base device + hba, must be first */#define sc_dev sc_hba.hba_dev struct sbusdev sc_sd; /* sbus device */ struct intrhand sc_ih; /* interrupt entry */ struct evcnt sc_intrcnt; /* interrupt counter */ struct dma_softc *sc_dc; /* pointer to corresponding dma sc */ /* * Addresses mapped to hardware registers. */ volatile struct espreg *sc_esp; volatile struct dmareg *sc_dma; /* * Copies of registers cleared/unlatched by reading. * (FIFO flags is not cleared, but we want it for debugging.) */ u_long sc_dmacsr; u_char sc_espstat; u_char sc_espstep; u_char sc_espintr; u_char sc_espfflags; /* miscellaneous */ int sc_clockfreq; /* clock frequency */ u_char sc_sel_timeout; /* select timeout */ u_char sc_id; /* initiator ID (default = 7) */ u_char sc_needclear; /* uncleared targets (1 bit each) */ u_char sc_esptype; /* 100, 100A, 2xx (see below) */ u_char sc_ccf; /* clock conversion factor */ u_char sc_conf1; /* value for config reg 1 */ u_char sc_conf2; /* value for config reg 2 */ u_char sc_conf3; /* value for config reg 3 */ struct bootpath *sc_bp; /* esp bootpath so far */ /* * Information pertaining to the current transfer, * including sequencing. * * The size of sc_msg is the size of the ESP fifo, * since we do message-in simply by allowing the fifo to fill. */ char sc_probing; /* used during autoconf; see below */ char sc_clearing; /* true => cmd is just to clear targ */ char sc_state; /* SCSI protocol state; see below */ char sc_sentcmd; /* set once we get cmd out */ char sc_dmaactive; /* true => doing dma */#ifdef notyet u_char sc_sync; /* synchronous transfer stuff (?) */#endif u_char sc_stat[2]; /* status from last `status' phase */ u_char sc_msg[16]; /* message from device */ u_short sc_dmactl; /* control to load into dma csr */ u_long sc_dmaaddr; /* address for next xfer */ int sc_dmasize; /* size of current xfer */ int sc_resid; /* count of bytes not yet xferred */ int sc_targ; /* the target involved */ struct scsi_cdb *sc_curcdb; /* ptr to current command */ /* might cdbspace eventually be per-target? */ struct scsi_cdb sc_cdbspace; /* space for one command */};/* * Values for sc_esptype (used to control configuration reset, and for * workarounds for chip bugs). The order is important; see espreset(). */#define ESP100 0#define ESP100A 1#define ESP2XX 2/* * Probe state. 0 means not probing. While looking for each target * we set this to PROBE_TESTING and do a TEST UNIT READY on unit 0. * If selection fails, this is changed to PROBE_NO_TARGET; otherwise * we assume the target exists, regardless of the result of the test. */#define PROBE_TESTING 1#define PROBE_NO_TARGET 2/* * States in sc_state. * * Note that S_SVC is rare: normally we load the SCSI command into the * ESP fifo and get interrupted only when the device has gone to data * or status phase. If the device wants to play games, though, we end * up doing things differently. */char *espstates[] = {#define S_IDLE 0 /* not doing anything */ "idle",#define S_SEL 1 /* expecting select done interrupt */ "selecting",#define S_SVC 2 /* expecting service req interrupt */ "waiting for svc req",#define S_DI 3 /* expecting data-in done interrupt */ "receiving data",#define S_DO 4 /* expecting data-out done interrupt */ "sending data",#define S_STAT 5 /* expecting status done interrupt */ "receiving status",#define S_MI 6 /* expecting message-in done interrupt */ "receiving message",#define S_FI 7 /* expecting final disconnect interrupt */ "waiting for disconnect"};/* * Hardware limits on transfer sizes (see comments at top). */#define ESPMAX (64 * 1024)#define DMAMAX(a) (0x01000000 - ((a) & 0x00ffffff))/* * Return values from espact(). */#define ACT_CONT 0 /* espact() handled everything */#define ACT_IO 1 /* espact() is xferring data */#define ACT_DONE 2 /* handled everything, and op is now done */#define ACT_ERROR 3 /* an error occurred, op has been trashed */#define ACT_RESET 4 /* please reset ESP, then do ACT_ERROR */#define ACT_QUICKINTR 5 /* another interrupt is expected immediately *//* autoconfiguration driver */void espattach(struct device *, struct device *, void *);struct cfdriver espcd = { NULL, "esp", matchbyname, espattach, DV_DULL, sizeof(struct esp_softc), "intr" };/* Sbus driver */void espsbreset(struct device *);/* interrupt interface */int espintr(void *);/* SCSI HBA driver */int espicmd(struct hba_softc *, int, struct scsi_cdb *, caddr_t, int, int);int espdump(struct hba_softc *, int, struct scsi_cdb *, caddr_t, int);void espstart(struct device *, struct sq *, struct buf *, scdgo_fn, struct device *);int espgo(struct device *, int, scintr_fn, struct device *, struct buf *, int);void esprel(struct device *);void esphbareset(struct hba_softc *, int);static struct hbadriver esphbadriver = { espicmd, espdump, espstart, espgo, esprel, esphbareset };/* other prototypes */static void espdoattach(int);static void dmareset(struct esp_softc *);static void espreset(struct esp_softc *, int);static void esperror(struct esp_softc *, const char *);static int espact(struct esp_softc *);void espselect(struct esp_softc *, int, struct scsi_cdb *);/* second arg to espreset() */#define RESET_ESPCHIP 0x1#define RESET_SCSIBUS 0x2#define RESET_BOTH (RESET_ESPCHIP | RESET_SCSIBUS)/* * Attach a found DMA chip. * The second argument is really a pointer to an sbus_attach_args. */voiddmaattach(parent, dev, args) struct device *parent; struct device *dev; void *args;{ register struct dma_softc *dc = (struct dma_softc *)dev; register struct sbus_attach_args *sa = args; register volatile struct dmareg *dma; register int rev; struct esp_softc *esc; if (sa->sa_ra.ra_vaddr) dma = (volatile struct dmareg *)sa->sa_ra.ra_vaddr; else dma = (volatile struct dmareg *) mapiodev(sa->sa_ra.ra_paddr, sizeof(struct dmareg)); dc->dc_dma = dma; switch (rev = DMA_REV(dma->dma_csr)) { case DMAREV_1: printf(": rev 1\n"); dc->dc_dmafmt = DMA_REV1_BITS; break; case DMAREV_2: printf(": rev 2\n"); dc->dc_dmafmt = DMA_REV2_BITS; break; case DMAREV_3: printf(": rev 3\n"); printf("WARNING: esp.c not yet updated for rev 3\n"); dc->dc_dmafmt = DMA_REV3_BITS; break; default: printf(": unknown revision code 0x%x\n", rev); dc->dc_dmafmt = DMA_REV3_BITS; /* cross fingers */ break; } dc->dc_dmarev = rev; espdoattach(dc->dc_dev.dv_unit);}/* * Attach a found ESP chip. Search for targets; attach each one found. * The latter must be deferred if the corresponding dma chip has not yet * been configured. */voidespattach(parent, self, args) struct device *parent; struct device *self; void *args;{ register struct esp_softc *sc = (struct esp_softc *)self; register struct sbus_attach_args *sa = args; register volatile struct espreg *esp; register struct bootpath *bp; int node, pri, freq, t; if (sa->sa_ra.ra_nintr != 1) { printf(": expected 1 interrupt, got %d\n", sa->sa_ra.ra_nintr); return; } pri = sa->sa_ra.ra_intr[0].int_pri; printf(" pri %d", pri); if (sa->sa_ra.ra_vaddr) esp = (volatile struct espreg *)sa->sa_ra.ra_vaddr; else esp = (volatile struct espreg *) mapiodev(sa->sa_ra.ra_paddr, sizeof(struct espreg)); sc->sc_esp = esp; node = sa->sa_ra.ra_node; sc->sc_id = getpropint(node, "initiator-id", 7); freq = getpropint(node, "clock-frequency", -1); if (freq < 0) freq = ((struct sbus_softc *)sc->sc_dev.dv_parent)->sc_clockfreq; /* MIGHT NEED TO RESET ESP CHIP HERE ...? */ /* * Find out whether we have a -100, -100A, or -2xx, * and what speed it runs at. */ sc->sc_conf1 = sc->sc_id | ESPCONF1_PARENB; /* sc->sc_conf2 = 0; */ /* sc->sc_conf3 = 0; */ esp->esp_conf1 = sc->sc_conf1; esp->esp_conf2 = 0; esp->esp_conf2 = ESPCONF2_SCSI2 | ESPCONF2_RPE; if ((esp->esp_conf2 & ~ESPCONF2_RSVD) != (ESPCONF2_SCSI2 | ESPCONF2_RPE)) { printf(": ESP100"); sc->sc_esptype = ESP100; } else { esp->esp_conf2 = 0; esp->esp_conf3 = 0; esp->esp_conf3 = 5; if (esp->esp_conf3 != 5) { /* XXX def bits */ printf(": ESP100A"); sc->sc_esptype = ESP100A; } else { esp->esp_conf3 = 0; printf(": ESP2XX"); sc->sc_esptype = ESP2XX; } } printf(", clock = %s MHz, ID = %d\n", clockfreq(freq), sc->sc_id); /* * Set clock conversion factor and select timeout. * N.B.: clock frequency is not actually used in the rest * of the driver; I calculate it here for completeness only * (so I can see it when debugging). */ sc->sc_clockfreq = freq; freq = howmany(freq, 1000 * 1000); /* convert to MHz */ t = ESPCCF_FROMMHZ(freq); if (t < ESPCCF_MIN) t = ESPCCF_MIN; sc->sc_ccf = t; t = ESPTIMO_REGVAL(250, t, freq); /* timeout = 250 ms. */ if (t >= 256) t = 0; sc->sc_sel_timeout = t; /* * Link into sbus; set interrupt handler. */ sc->sc_sd.sd_reset = espsbreset; sbus_establish(&sc->sc_sd, &sc->sc_dev); sc->sc_ih.ih_fun = espintr; sc->sc_ih.ih_arg = sc; intr_establish(pri, &sc->sc_ih); evcnt_attach(&sc->sc_dev, "intr", &sc->sc_intrcnt);#define SAME_ESP(bp, sa) \ ((bp->val[0] == sa->sa_slot && bp->val[1] == sa->sa_offset) || \
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -