📄 ncr.c
字号:
/* @(#)ncr.c 1.1 92/07/30 SMI *//* * Copyright (c) 1989 by Sun Microsystems, Inc. */#include "ncr.h"#if NNCR > 0#ifndef lintstatic char sccsid[] = "@(#)ncr.c 1.1 92/07/30";#endif lint/* * Features of this version: * * A. Set up dma before any possibility of going into a DATA PHASE. * This means that the dma engine must be setup: * * 1: prior to dropping SEL and deasserting NCR_ICR_DATA * in the selection process * * 2: prior to acknowledging any message (including the IDENTIFY * message received during reselection). * * Ick. This must be done because this way because the 3/50 && 3/60 * DMA engines don't work on sending out the SCSI bus otherwise. * * B. We get mostly PHASE NOT MATCHED interrupts on other than DATA PHASE. * * C. Linked commands now work */#include <scsi/scsi.h>#include <scsi/adapters/ncrreg.h>#include <sys/mman.h>#include <machine/pte.h>#include <machine/cpu.h>#include <vm/seg.h>/* * Debugging macros */int ncr_debug = 0;#define PRINTF1 if (ncr_debug) printf#define PRINTF2 if (ncr_debug > 1) printf#define PRINTF3 if (ncr_debug > 2) printf/* #define EPRINTF if (scsi_options & SCSI_DEBUG_HA) printf */#define EPRINTF printf#define INFORMATIVE (ncr_debug)/* * Short hand defines */#define UNDEFINED -1#define CURRENT_CMD(ncr) ((ncr)->n_slots[(ncr)->n_cur_slot])#define SLOT(sp) ((short)(Tgt((sp))<<3|(Lun((sp)))))#define NEXTSLOT(slot) ((slot)+1) & ((NTARGETS*NLUNS_PER_TARGET)-1)#define Tgt(sp) ((sp)->cmd_pkt.pkt_address.a_target)#define Lun(sp) ((sp)->cmd_pkt.pkt_address.a_lun)#define NON_INTR(sp) ((sp->cmd_pkt.pkt_flags & FLAG_NOINTR) != 0)#define CNAME ncrdriver.mdr_cname#define CNUM (ncr-ncr_softc)/* * Global function declarations */void ncrintr();/* * Local function declarations */static int ncr_start(), ncr_abort(), ncr_reset(), ncr_getcap(), ncr_setcap();static int ncr_NACKmsg(), ncr_ACKmsg(), ncr_sbcwait(), ncr_csrwait();static int ncr_xfrin(), ncr_xfrin_noack();static int ncr_ustart();static void ncr_phasemanage();static int ncr_getphase();static int ncr_dopoll(), ncrpoll();static void ncrsvc();static int ncr_select(), ncr_reselect();static void ncr_preempt(), ncr_disconnect();static int ncr_sendcmd(), ncr_sendmsg(), ncr_recvmsg(), ncr_senddata();static int ncr_recvdata(), ncr_recvstatus();static void ncr_finish();static int ncr_watch();static void ncr_printstate(), ncr_curcmd_timeout(), ncr_internal_abort();static void ncr_do_abort(), ncr_internal_reset(), ncr_hw_reset();static void ncr_dump_datasegs();static caddr_t ncr_mapdmabuf();/* * DMA function declarations */static int ncr_flushbyte(), ncr_dma_wait(), ncr_dma_cleanup();static void ncr_dma_enable(), ncr_dma_setup();static int ncr_vme_dma_cleanup(), ncr_vme_waitdma(), ncr_vme_dma_recv();#ifdef sun4static int ncr_cobra_dma_cleanup();static int ncr_cobra_dma_chkerr(), ncr_cobra_dma_recv();#endif sun4#ifdef sun3static int ncr_ob_dma_cleanup();static void ncr_ob_dma_setup();#endifstatic int ncr_3e_dma_cleanup();static int ncr_3e_fetchdata(), ncr_3e_storedata();/* * Local static data */static struct ncr *ncr_softc = (struct ncr *) 0;static long Dmabase; /* * Base address to stuff into DMA engine * and or into the assumed offset from DVMA * that is in the dma cookies we get. */static char *Cname; /* * pointer to controller name- copied here so that * error printouts don't generated long sequences * of structure pointer de-references. */static int ncr_arbitration_delay = NCR_ARBITRATION_DELAY;static int ncr_bus_clear_delay = NCR_BUS_CLEAR_DELAY;static int ncr_bus_settle_delay = NCR_BUS_SETTLE_DELAY;static char *cbsr_bits = CBSR_BITS;/* * Probe, Slave and Attach routines */static int ncr_probe(), ncr_slave(), ncr_attach();static int nncr = NNCR;struct mb_ctlr *ncrctlr[NNCR];struct mb_driver ncrdriver = { ncr_probe, ncr_slave, ncr_attach, 0, 0, ncrpoll, NCR_HWSIZE, "scsibus", 0, "ncr", ncrctlr, MDR_BIODMA};static intncr_probe(reg, ctlr)u_short *reg;int ctlr;{ register struct ncrsbc *ncrp = (struct ncrsbc *) reg; register struct ncr *ncr; long val; u_long dma_base, sbcaddr, ctladdr, dmaaddr;#ifdef sun3 u_long udcp;#endif caddr_t dmabuf; int host_type = -1; u_short *shortp; /* * Check for ncr 5380 Scsi Bus Ctlr chip with "peekc()"; struct * ncr-5380 is common to all onboard scsi and vme scsi board. if not * exist, return 0. */ PRINTF3("ncr_probe: reg= %x virt, %x phys\n", reg, getdevaddr((caddr_t) reg)); /* * We can use the first byte address of reg, 'coz we'll guarantee * (by the time we are through) that that will always be the first * byte of the NCR 5380 SBC (which should be the cbsr register). */ if (peekc((caddr_t)reg) == -1) { return (0); } sbcaddr = (u_long) reg; dmaaddr = ((u_long) reg) + sizeof (struct ncrsbc); /* * probe for different host adaptor interfaces */ switch (cpu) {#ifdef sun4 case CPU_SUN4_110: /* * probe for 4/110 dma interface */ ctladdr = ((u_long) reg) + COBRA_CSR_OFF; if (peekl((long *) (dmaaddr), (long *) &val) == -1) { return (0); } host_type = IS_COBRA; dma_base = DMA_BASE_110; break;#endif sun4#ifdef sun3 case CPU_SUN3_50: case CPU_SUN3_60: ctladdr = ((u_long) reg) + CSR_OFF; if (peek((short *)& ((struct ncrdma *)dmaaddr)->udc_rdata) == -1) return (0); udcp = (u_long) IOPBALLOC(sizeof (struct udc_table)); if (udcp == 0 || udcp & 0x3) { printf("%s%d: no udc table\n", CNAME, ctlr); if (udcp) IOPBFREE(udcp, sizeof (struct udc_table)); return (0); } host_type = IS_3_50; dma_base = DMA_BASE_3_50; break;#endif sun3 default: /* * This is either a SCSI-3 or a 3/E VME board * * First see whether the dma address register is there * The low 16 bits is common across all platforms. * */ if (peek((short *) (dmaaddr + 2)) == -1) { return (0); } /* * Okay, now whether it is a 3/E board */ ctladdr = ((u_long) reg) + SUN3E_CSR_OFF; if (peek((short *) (ctladdr + 2)) != -1) { if (poke ((short *) (ctladdr + 2), 0) == 0) { if (peek((short *) (ctladdr + 2)) == 0x402) { dmabuf = ncr_mapdmabuf(reg); if (dmabuf == (caddr_t) 0) return (0); host_type = IS_3E; dma_base = 0; break; } } } /* * Okay- it wasn't a 3/E. Now see whether it's a scsi-3 board. * * Make sure that it isn't a SCSI-2 board (which occupies 4k * of VME space instead of the 2k that the SCSI-3 occupies). * (the above is a quote from si.c. The code below doesn't * seem to bear this out exactly). * */ if (peek((short *) (((u_long) reg) + 0x800)) != -1) { return (0); } /* * Make sure that we're cool (really a scsi-3 board). */ ctladdr = ((u_long) reg) + CSR_OFF; if (peek((short *) (ctladdr + 2)) == -1) return (0); if ((((struct ncrctl *) ctladdr)->csr.lsw & NCR_CSR_ID) == 0) { printf("%s%d: unmodified scsi-3 board- you lose..\n", CNAME, ctlr); return (0); } host_type = IS_SCSI3; dma_base = 0; break; } /* * Establish initial softc values */ if (ncr_softc == 0) { ncr_softc = (struct ncr *) kmem_zalloc((unsigned) (NNCR * sizeof (struct ncr))); if (ncr_softc == 0) return (0); Dmabase = dma_base; timeout(ncr_watch, (caddr_t) 0, hz); } /* * initialize software structure */ ncr = &ncr_softc[ctlr]; /* * Allocate a page for being able to * flush the last bit of a data transfer. */ ncr->n_kment = rmalloc(kernelmap, mmu_btopr(MMU_PAGESIZE)); if (ncr->n_kment == 0) { return (0); } ncr->n_dev = &ncrdriver; ncr->n_id = NCR_HOST_ID; ncr->n_sbc = (struct ncrsbc *) (sbcaddr); ncr->n_dma = (struct ncrdma *) (dmaaddr); ncr->n_ctl = (struct ncrctl *) (ctladdr); ncr->n_type = host_type; ncr->n_tran.tran_start = ncr_start; ncr->n_tran.tran_abort = ncr_abort; ncr->n_tran.tran_reset = ncr_reset; ncr->n_tran.tran_getcap = ncr_getcap; ncr->n_tran.tran_setcap = ncr_setcap; ncr->n_tran.tran_pktalloc = scsi_std_pktalloc; ncr->n_tran.tran_dmaget = scsi_std_dmaget; ncr->n_tran.tran_pktfree = scsi_std_pktfree; ncr->n_tran.tran_dmafree = scsi_std_dmafree; ncr->n_last_slot = ncr->n_cur_slot = UNDEFINED; switch (host_type) {#ifdef sun4 case IS_COBRA: ncr->n_dma_setup = ncr_dma_setup; ncr->n_dma_cleanup = ncr_cobra_dma_cleanup; break;#endif sun4#ifdef sun3 case IS_3_50: ncr->n_dma_setup = ncr_ob_dma_setup; ncr->n_dma_cleanup = ncr_ob_dma_cleanup; ncr->n_udc = (struct udc_table *) udcp; break;#endif sun3 case IS_SCSI3: ncr->n_dma_setup = ncr_dma_setup; ncr->n_dma_cleanup = ncr_vme_dma_cleanup; break; case IS_3E: ncr->n_dmabuf = dmabuf; ncr->n_dma_setup = ncr_dma_setup; ncr->n_dma_cleanup = ncr_3e_dma_cleanup; break; } ncr_internal_reset(ncr, NCR_RESET_ALL, RESET_NOMSG); return (NCR_HWSIZE);}/*ARGSUSED*/static intncr_slave(md, reg)struct mb_device *md;caddr_t reg;{ struct ncr *ncr = &ncr_softc[md->md_unit]; md->md_slave = ncr->n_id << 3; return (TRUE);}static intncr_attach(md)struct mb_device *md;{ int i; static int ncr_spl = 0; struct ncr *ncr = &ncr_softc[md->md_unit]; /* * calculate max spl value so far seen and the default config_value */ ncr_spl = MAX(ncr_spl, pritospl(md->md_intpri)); /* * make sure that everyone, including us, is locking at the same * priority */ for (i = 0; i <= md->md_unit; i++) { ncr_softc[i].n_tran.tran_spl = ncr_spl; } /* * Initialize interrupt vector register (if any) */ if (IS_VME(ncr->n_type) && md->md_mc->mc_intr) { if (ncr->n_type == IS_3E) { N_CTL->ivec2 = (md->md_mc->mc_intr->v_vec & 0xff); } else { N_CTL->iv_am = (md->md_mc->mc_intr->v_vec & 0xff) | VME_SUPV_DATA_24; } *(md->md_mc->mc_intr->v_vptr) = (int) ncr; } /* * Now make ourselves known to the rest of the SCSI world */ scsi_config(&ncr->n_tran, md);}/* * map in 3/E dma buffer. This buffer resides in the * 64kb below the base address of the NCR 5380 registers. * * * XXX: We're cheating here because we know what address space * XXX: the 3/E board resides in, plus we know that the dma buffer * XXX: is on a page boundary. * XXX: * XXX: We need to do a better job than that on this! */static caddr_tncr_mapdmabuf(sbcreg)u_short *sbcreg;{ extern int getdevaddr(); extern struct seg kseg; u_long addr, pageval; u_short *reg; long a; addr = getdevaddr((caddr_t) sbcreg) - (64 * 1024); if (addr & MMU_PAGEOFFSET) { return ((caddr_t) 0); }#ifdef sun3x pageval = btop(VME24D16_BASE + (addr & VME24D16_MASK));#else sun3x pageval = PGT_VME_D16 | btop(VME24_BASE | (addr & VME24_MASK));#endif sun3x a = rmalloc(kernelmap, btoc(64 * 1024)); if (a == 0) return ((caddr_t) 0); reg = (u_short *) ((int) kmxtob(a)); segkmem_mapin(&kseg, (addr_t) reg, (u_int) (64 * 1024), PROT_READ | PROT_WRITE, pageval, 0); if (peek((short *)reg) < 0 || poke((short *)reg, (short) 0xa55e) || peek((short *)reg) != 0xa55e) { rmfree (kernelmap, btoc (64 * 1024), (u_long) a); return ((caddr_t) 0); } return ((caddr_t) reg);}/* * External Interface Routines */static intncr_start(sp)register struct scsi_cmd *sp;{ register struct ncr *ncr; register int s; short slot; /* * get scsi address based on the "cookie" in cmd_packet */ ncr = (struct ncr *)sp->cmd_pkt.pkt_address.a_cookie; slot = ((Tgt(sp)*NLUNS_PER_TARGET) | Lun(sp)); s = splr(ncr->n_tran.tran_spl); if (ncr->n_slots[slot] != (struct scsi_cmd *) 0) { PRINTF3("ncr_start: queuing error\n"); (void) splx(s); return (FALSE); } /* * reinitialize some fields in the 'cmd_pkt' that need it... * if upper level requests watchdog, set timeout_cnt and flag */ sp->cmd_pkt.pkt_resid = 0; sp->cmd_pkt.pkt_reason = sp->cmd_pkt.pkt_state = sp->cmd_pkt.pkt_statistics = 0; sp->cmd_cdbp = (caddr_t) sp->cmd_pkt.pkt_cdbp; sp->cmd_scbp = sp->cmd_pkt.pkt_scbp; *sp->cmd_scbp = 0; /* clear status byte array */ if (sp->cmd_timeout = sp->cmd_pkt.pkt_time) sp->cmd_flags |= CFLAG_WATCH; else sp->cmd_flags &= ~CFLAG_WATCH; sp->cmd_data = sp->cmd_saved_data = sp->cmd_mapping; sp->cmd_subseg.d_base = sp->cmd_mapping; sp->cmd_subseg.d_count = 0; sp->cmd_subseg.d_next = (struct dataseg *) 0; sp->cmd_cursubseg = &sp->cmd_subseg; sp->cmd_flags &= ~(CFLAG_NEEDSEG|CFLAG_CMDDISC); /* * If this is a non-interrupting command, don't allow disconnects */ if (NON_INTR(sp)) { sp->cmd_pkt.pkt_flags |= FLAG_NODISCON; } /* * accept the command by setting the req job_ptr in appropriate slot */ ncr->n_ncmds++; ncr->n_slots[slot] = sp; if ((sp->cmd_pkt.pkt_flags & FLAG_NOINTR) == 0) { if ((ncr->n_npolling == 0) && (ncr->n_state == STATE_FREE)) { (void) ncr_ustart(ncr, slot); } } else if (ncr->n_state == ACTS_ABORTING) { printf("%s%d: unable to start non-intr cmd\n", CNAME, CNUM); (void) splx(s); return (FALSE); } else { /* * Wait till all current commands completed with STATE_FREE by * "ncr_dopoll()", then fire off the job, check if preempted * right away, try again; accept the command */ PRINTF3("ncr_start: poll cmd, state= %x\n", ncr->n_state); ncr->n_npolling++; while (ncr->n_npolling != 0) { /* * drain any current active command(s) */ while (ncr->n_state != STATE_FREE) { if (ncr_dopoll(ncr)) { (void) splx(s); return (FALSE); } } /* * were we preempted by a reselect coming back in? * If so, 'round we go again.... */ PRINTF3("ncr_start: ready to start cmd\n"); if (ncr_ustart(ncr, slot) == FALSE) continue; /* * Okay, now we're 'running' this command.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -