diff --git a/sys/arch/pc532/dev/ncr.c b/sys/arch/pc532/dev/ncr.c index b294c01988fa..6bc822aa6421 100644 --- a/sys/arch/pc532/dev/ncr.c +++ b/sys/arch/pc532/dev/ncr.c @@ -1,8 +1,7 @@ -/* $NetBSD: ncr.c,v 1.17 1995/08/12 20:31:13 mycroft Exp $ */ +/* $NetBSD: ncr.c,v 1.18 1995/08/25 07:30:33 phil Exp $ */ /* - * Copyright (c) 1995 Leo Weppelman. - * PC532-Port by Matthias Pfaller. + * Copyright (c) 1994 Matthias Pfaller. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -15,7 +14,7 @@ * 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 Leo Weppelman. + * This product includes software developed by Matthias Pfaller. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * @@ -29,6 +28,8 @@ * 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. + * + * $Id: ncr.c,v 1.18 1995/08/25 07:30:33 phil Exp $ */ #include @@ -39,1856 +40,160 @@ #include #include #include +#include + +/* + * Include the driver definitions + */ +#include "ncr5380reg.h" #include "ncrreg.h" /* - * SCSI completion status codes, should move to sys/scsi/???? + * Set the various driver options */ -#define SCSMASK 0x1e /* status code mask */ -#define SCSGOOD 0x00 /* good status */ -#define SCSCHKC 0x02 /* check condition */ -#define SCSBUSY 0x08 /* busy status */ -#define SCSCMET 0x04 /* condition met / good */ - -/********************************************************************/ - #define NREQ 18 /* Size of issue queue */ #define AUTO_SENSE 1 /* Automatically issue a request-sense */ -#undef DBG_SEL /* Show the selection process */ -#undef DBG_REQ /* Show enqueued/ready requests */ -#undef DBG_NOWRITE /* Do not allow writes to the targets */ -#undef DBG_PIO /* Show the polled-I/O process */ -#undef DBG_INF /* Show information transfer process */ -#undef DBG_NOSTATIC /* No static functions, all in DDB trace*/ -#undef DBG_PID /* Keep track of driver */ -#undef REAL_DMA /* Use DMA if sensible */ -#undef REAL_DMA_POLL 0 /* 1: Poll for end of DMA-transfer */ - -#ifdef DBG_NOSTATIC -# define static -#endif -#ifdef DBG_SEL -# define DBG_SELPRINT(a,b) printf(a,b) -#else -# define DBG_SELPRINT(a,b) -#endif -#ifdef DBG_PIO -# define DBG_PIOPRINT(a,b,c) printf(a,b,c) -#else -# define DBG_PIOPRINT(a,b,c) -#endif -#ifdef DBG_INF -# define DBG_INFPRINT(a,b,c) a(b,c) -#else -# define DBG_INFPRINT(a,b,c) -#endif -#ifdef DBG_PID - static char *last_hit = NULL; -# define PID(a) last_hit = a -#else -# define PID(a) -#endif +#define DRNAME ncr /* used in various prints */ +#undef DBG_SEL /* Show the selection process */ +#undef DBG_REQ /* Show enqueued/ready requests */ +#undef DBG_NOWRITE /* Do not allow writes to the targets */ +#undef DBG_PIO /* Show the polled-I/O process */ +#undef DBG_INF /* Show information transfer process */ +#undef DBG_NOSTATIC /* No static functions, all in DDB trace*/ +#define DBG_PID /* Keep track of driver */ +#undef REAL_DMA /* Use DMA if sensible */ +#undef REAL_DMA_POLL 0 /* 1: Poll for end of DMA-transfer */ +#define USE_PDMA /* Use special pdma-transfer function */ /* - * Return values of check_intr() + * Softc of currently active controller (a bit of fake; we only have one) */ -#define INTR_SPURIOUS 0 -#define INTR_RESEL 2 -#define INTR_DMA 3 - -/* - * Set bit for target when parity checking must be disabled. - * My (LWP) Maxtor 7245S seems to generate parity errors on about 50% - * of all transfers while the data is correct!? - */ -u_char ncr5380_no_parchk = 0; - -/* - * This is the default sense-command we send. - */ -static u_char sense_cmd[] = { - REQUEST_SENSE, 0, 0, 0, sizeof(struct scsi_sense), 0 -}; - -/* - * True if the main co-routine is running - */ -static volatile int main_running = 0; - -/* - * Mask of targets selected - */ -u_char busy; - -struct ncr_softc { - struct device sc_dev; - struct scsi_link sc_link; -}; - -static void ncr5380_minphys(struct buf *bp); -static int ncr5380_scsi_cmd(struct scsi_xfer *xs); -static int ncr5380_show_scsi_cmd(struct scsi_xfer *xs); - -struct scsi_adapter ncr5380_switch = { - ncr5380_scsi_cmd, /* scsi_cmd() */ - ncr5380_minphys, /* scsi_minphys() */ - 0, /* open_target_lu() */ - 0 /* close_target_lu() */ -}; - -struct scsi_device ncr5380_dev = { - NULL, /* use default error handler */ - NULL, /* do not have a start function */ - NULL, /* have no async handler */ - NULL /* Use default done routine */ -}; - -/* - * Max. number of dma-chains per request - */ -#define MAXDMAIO (MAXPHYS/NBPG + 1) - -/* - * Some requests are not contiguous in physical memory. We need to break them - * up into contiguous parts for DMA. - */ -struct dma_chain { - u_int dm_count; - u_long dm_addr; -}; - -/* - * Define our issue, free and disconnect queue's. - */ -typedef struct req_q { - struct req_q *next; /* next in free, issue or discon queue */ - struct req_q *link; /* next linked command to execute */ - struct scsi_xfer *xs; /* request from high-level driver */ - u_char dr_flag; /* driver state */ - u_char phase; /* current SCSI phase */ - u_char msgout; /* message to send when requested */ - u_char targ_id; /* target for command */ - u_char targ_lun; /* lun for command */ - u_char status; /* returned status byte */ - u_char message; /* returned message byte */ -#ifdef REAL_DMA - struct dma_chain dm_chain[MAXDMAIO]; - struct dma_chain *dm_cur; /* current dma-request */ - struct dma_chain *dm_last; /* last dma-request */ -#endif - long xdata_len; /* length of transfer */ - u_char *xdata_ptr; /* physical address of transfer */ - struct scsi_generic xcmd; /* command to execute */ -} SC_REQ; - -/* - * Values for dr_flag: - */ -#define DRIVER_IN_DMA 1 /* Non-polled DMA activated */ -#define DRIVER_AUTOSEN 2 /* Doing automatic sense */ -#define DRIVER_NOINT 4 /* We are booting: no interrupts */ -#define DRIVER_DMAOK 8 /* DMA can be used on this request */ - - -static SC_REQ req_queue[NREQ]; -static SC_REQ *free_head = NULL; /* Free request structures */ -static SC_REQ *issue_q = NULL; /* Commands waiting to be issued*/ -static SC_REQ *discon_q = NULL; /* Commands disconnected */ -static SC_REQ *connected = NULL; /* Command currently connected */ +static struct ncr_softc *cur_softc; +static int callback_scheduled; /* Atari artefact :-) */ /* * Function decls: */ -static int transfer_pio __P((u_char *, u_char *, u_long *)); static void transfer_pdma __P((u_char *, u_char *, u_long *)); -static int wait_req_true __P((void)); -static int wait_req_false __P((void)); -static int scsi_select __P((SC_REQ *, int)); -static int handle_message __P((SC_REQ *, u_int)); -static int information_transfer __P((void)); -static void reselect __P((void)); -static int dma_ready __P((int, int)); -static int check_autosense __P((SC_REQ *, int)); -static int reach_msg_out __P((u_long)); -static void timer __P((void)); -static int check_intr __P((void)); -static void scsi_reset __P((void)); -static void scsi_main __P((void)); -#ifdef REAL_DMA -static int scsi_dmaok __P((SC_REQ *)); -#else -#define scsi_dmaok(x) 0 -#endif -static void run_main __P((void)); static void ncr_intr __P((void *)); -static void show_request __P((SC_REQ *, char *)); -static void show_phase __P((SC_REQ *, int)); -static void show_signals __P((void)); +static void ncr_soft_intr __P((void *)); +#define scsi_dmaok(x) 0 +#define fair_to_keep_dma() 1 +#define claimed_dma() 1 +#define reconsider_dma() +#define ENABLE_NCR5380(sc) do { \ + scsi_select_ctlr(DP8490); \ + cur_softc = sc; \ + } while (0) void -delay(int timeo) +delay(timeo) + int timeo; { int len; - for (len=0;len> 4) & 0xf) { - case 0: - case 1: - return(6); - case 2: - case 3: - return(10); - } - return(12); -} - -/* - * Wait for request-line to become active. When it doesn't return 0. - * Otherwise return != 0. - * The timeouts in the 'wait_req_*' functions are arbitrary and rather - * large. In 99% of the invocations nearly no timeout is needed but in - * some cases (especially when using my tapedrive, a Tandberg 3600) the - * device is busy internally and the first SCSI-phase will be delayed. - */ -extern __inline__ int wait_req_true(void) -{ - int timeout = 25000; - - while(!(SCSI_5380->scsi_idstat & SC_S_REQ) && --timeout) - delay(1); - return(SCSI_5380->scsi_idstat & SC_S_REQ); -} - -/* - * Wait for request-line to become inactive. When it doesn't return 0. - * Otherwise return != 0. - */ -extern __inline__ int wait_req_false(void) -{ - int timeout = 25000; - - while((SCSI_5380->scsi_idstat & SC_S_REQ) && --timeout) - delay(1); - return(!(SCSI_5380->scsi_idstat & SC_S_REQ)); -} - -extern __inline__ void finish_req(SC_REQ *reqp) -{ - int sps; - struct scsi_xfer *xs = reqp->xs; - - /* - * Return request to free-q - */ - sps = splbio(); - reqp->next = free_head; - free_head = reqp; - splx(sps); - - xs->flags |= ITSDONE; - scsi_done(xs); -} - -/* - * Auto config stuff.... - */ -void ncr_attach __P((struct device *, struct device *, void *)); -int ncr_match __P((struct device *, struct cfdata *, void *)); - -struct cfdriver ncrcd = { - NULL, "ncr", (cfmatch_t)ncr_match, ncr_attach, - DV_DULL, sizeof(struct ncr_softc), NULL, 0 }; - -int -ncr_match(pdp, cdp, auxp) -struct device *pdp; -struct cfdata *cdp; -void *auxp; +static int +machine_match(pdp, cdp, auxp, cfd) + struct device *pdp; + struct cfdata *cdp; + void *auxp; + struct cfdriver *cfd; { if(cdp->cf_unit != 0) /* Only one unit */ return(0); return(1); } -void -ncr_attach(pdp, dp, auxp) -struct device *pdp, *dp; -void *auxp; +static void +scsi_mach_init(sc) + struct ncr_softc *sc; { - struct ncr_softc *sc; - int i; - - sc = (struct ncr_softc *)dp; - - sc->sc_link.adapter_softc = sc; - sc->sc_link.adapter_target = 7; - sc->sc_link.adapter = &ncr5380_switch; - sc->sc_link.device = &ncr5380_dev; - sc->sc_link.openings = NREQ - 1; - - /* - * Initialize request queue freelist. - */ - for(i = 0; i < NREQ; i++) { - req_queue[i].next = free_head; - free_head = &req_queue[i]; - } - - /* - * Initialize the host adapter - */ - SCSI_5380->scsi_icom = 0; - SCSI_5380->scsi_mode = IMODE_BASE; - SCSI_5380->scsi_tcom = 0; - SCSI_5380->scsi_idstat = 0; + register int i; intr_disable(IR_SCSI1); - i = intr_establish(SOFTINT, run_main, NULL, "softncr", IPL_BIO, 0); - intr_establish(IR_SCSI1, ncr_intr, (void *)i, "ncr", IPL_BIO, RAISING_EDGE); - printf(" addr 0x%x, irq %d\n", SCSI_5380, IR_SCSI1); - - /* - * attach all scsi units on us - */ - config_found(dp, &sc->sc_link, NULL); - intr_enable(IR_SCSI1); + i = intr_establish(SOFTINT, ncr_soft_intr, sc, "softncr", IPL_BIO, 0); + intr_establish(IR_SCSI1, ncr_intr, (void *)i, "ncr", IPL_BIO, RISING_EDGE); + printf(" addr 0x%x, irq %d", NCR5380, IR_SCSI1); } /* - * End of auto config stuff.... - */ - -/* - * Carry out a request from the high level driver. - */ -static int -ncr5380_scsi_cmd(struct scsi_xfer *xs) -{ - int sps; - SC_REQ *reqp; - int flags = xs->flags; - - /* - * Sanity check on flags... - */ - if(flags & ITSDONE) { - printf("ncr5380_scsi_cmd: command already done.....\n"); - xs->flags &= ~ITSDONE; - } - if(!(flags & INUSE)) { - printf("ncr5380_scsi_cmd: command not in use.....\n"); - xs->flags |= ~INUSE; - } - - /* - * We do not queue RESET commands - */ - if(flags & SCSI_RESET) { - scsi_reset(); - return(COMPLETE); - } - - /* - * Get a request block - */ - sps = splbio(); - if((reqp = free_head) == 0) { - splx(sps); - return(TRY_AGAIN_LATER); - } - free_head = reqp->next; - reqp->next = NULL; - splx(sps); - - /* - * Initialize our private fields - */ - reqp->dr_flag = (xs->flags & SCSI_POLL) ? DRIVER_NOINT : 0; - reqp->phase = NR_PHASE; - reqp->msgout = MSG_NOOP; - reqp->status = SCSGOOD; - reqp->link = NULL; - reqp->xs = xs; - reqp->targ_id = xs->sc_link->target; - reqp->targ_lun = xs->sc_link->lun; - reqp->xdata_ptr = (u_char*)xs->data; - reqp->xdata_len = xs->datalen; - memcpy(&reqp->xcmd, xs->cmd, sizeof(struct scsi_generic)); - reqp->xcmd.bytes[0] |= xs->sc_link->lun << 5; - - /* - * Check if DMA can be used on this request - */ - if(!(xs->flags & SCSI_POLL) && scsi_dmaok(reqp)) - reqp->dr_flag |= DRIVER_DMAOK; - - /* - * Insert the command into the issue queue. Note that 'REQUEST SENSE' - * commands are inserted at the head of the queue since any command - * will clear the existing contingent allegience condition and the sense - * data is only valid while the condition exists. - * When possible, link the command to a previous command to the same - * target. This is not very sensible when AUTO_SENSE is not defined! - * Interrupts are disabled while we are fiddling with the issue-queue. - */ - sps = splbio(); - if((issue_q == NULL) || (reqp->xcmd.opcode == REQUEST_SENSE)) { - reqp->next = issue_q; - issue_q = reqp; - } - else { - SC_REQ *tmp, *link; - - tmp = issue_q; - link = NULL; - do { - if(!link && (tmp->targ_id == reqp->targ_id) && !tmp->link) - link = tmp; - } while(tmp->next && (tmp = tmp->next)); - tmp->next = reqp; -#ifdef AUTO_SENSE - if(link) { - link->link = reqp; - link->xcmd.bytes[link->xs->cmdlen-1] |= 1; - } -#endif - } - splx(sps); - -#ifdef DBG_REQ - show_request(reqp,(reqp->xcmd.opcode == REQUEST_SENSE) ? "HEAD":"TAIL"); -#endif - - run_main(); - if(xs->flags & SCSI_POLL) - return(COMPLETE); /* We're booting */ - return(SUCCESSFULLY_QUEUED); -} - -#define MIN_PHYS 65536 /*BARF!!!!*/ -static void -ncr5380_minphys(struct buf *bp) -{ - if(bp->b_bcount > MIN_PHYS) { - printf("Uh-oh... ncr5380_minphys setting bp->b_bcount=%x.\n",MIN_PHYS); - bp->b_bcount = MIN_PHYS; - } - minphys(bp); -} -#undef MIN_PHYS - -static int -ncr5380_show_scsi_cmd(struct scsi_xfer *xs) -{ - u_char *b = (u_char *) xs->cmd; - int i = 0; - - if(!(xs->flags & SCSI_RESET)) { - printf("ncr5380(%d:%d:%d,0x%x)-", xs->sc_link->scsibus, - xs->sc_link->target, xs->sc_link->lun, xs->sc_link->flags); - while(i < xs->cmdlen) { - if(i) - printf(","); - printf("%x",b[i++]); - } - printf("-\n"); - } - else { - printf("ncr5380(%d:%d:%d)-RESET-\n", - xs->sc_link->scsibus,xs->sc_link->target, xs->sc_link->lun); - } -} - -/* - * The body of the driver. + * 5380 interrupt. */ static void -scsi_main() +ncr_intr(softint) + void *softint; { - SC_REQ *req, *prev; - int itype; - int sps; + int ctrlr; - /* - * While running in the driver SCSI-interrupts are disabled. - */ - intr_disable(IR_SCSI1); - - scsi_select_ctlr(DP8490); - PID(")"); - for(;;) { - sps = splbio(); - if(!connected) { - - /* - * Search through the issue-queue for a command - * destined for a target that isn't busy. - */ - prev = NULL; - for(req=issue_q; req != NULL; prev = req, req = req->next) { - if(!(busy & (1 << req->targ_id))) { - /* - * Found one, remove it from the issue queue - */ - if(prev == NULL) - issue_q = req->next; - else prev->next = req->next; - req->next = NULL; - break; - } - } - - /* - * When a request has just ended, we get here before an other - * device detects that the bus is free and that it can - * reconnect. The problem is that when this happens, we always - * baffle the device because our (initiator) id is higher. This - * can cause a sort of starvation on slow devices. So we check - * for a pending reselection here. - * Note that 'connected' will be non-null if the reselection - * succeeds. - */ - if((SCSI_5380->scsi_idstat&(SC_S_SEL|SC_S_IO)) - == (SC_S_SEL|SC_S_IO)){ - if(req != NULL) { - req->next = issue_q; - issue_q = req; - } - splx(sps); - - reselect(); - SC_CLINT; - goto connected; - } - - /* - * The host is not connected and there is no request - * pending, exit. - */ - if(req == NULL) { - PID("{"); - goto main_exit; - } - - /* - * Re-enable interrupts before handling the request. - */ - splx(sps); - -#ifdef DBG_REQ - show_request(req, "TARGET"); -#endif - /* - * We found a request. Try to connect to the target. If the - * initiator fails arbitration, the command is put back in the - * issue queue. - */ - if(scsi_select(req, 0)) { - sps = splbio(); - req->next = issue_q; - issue_q = req; - splx(sps); -#ifdef DBG_REQ - printf("Select failed on target %d\n", req->targ_id); -#endif - } - } - else splx(sps); -connected: - if(connected) { - /* - * If the host is currently connected but a 'real-dma' transfer - * is in progress, the 'end-of-dma' interrupt restarts main. - * So quit. - */ - sps = splbio(); - if(connected && (connected->dr_flag & DRIVER_IN_DMA)) { - PID("["); - goto main_exit; - } - splx(sps); - - /* - * Let the target guide us through the bus-phases - */ - while(information_transfer() == -1) - ; - } - } - /* NEVER TO REACH HERE */ - panic("ncr0: not designed to come here"); - -main_exit: - /* - * We enter here with interrupts disabled. We are about to exit main - * so interrupts should be re-enabled. Because interrupts are edge - * triggered, we could already have missed the interrupt. Therefore - * we check the IRQ-line here and re-enter when we really missed a - * valid interrupt. - */ - PID("S"); - intr_enable(IR_SCSI1); - SCSI_5380->scsi_idstat = SC_HOST_ID; - if(SCSI_5380->scsi_dmstat & SC_IRQ_SET) { - if((itype = check_intr()) != INTR_SPURIOUS) { - intr_disable(IR_SCSI1); - splx(sps); - - if(itype == INTR_RESEL) - reselect(); - else dma_ready(0, 0); - SC_CLINT; - goto connected; - } - } - main_running = 0; - PID("R"); - splx(sps); -} - -/* - * The SCSI-controller interrupt. This interrupt occurs on reselections and - * at the end of non-polled DMA-interrupts. - */ -static void -ncr_intr(void *softint) -{ - int itype; - int dma_done; - int ctrlr; - - /* PID("$"); */ ctrlr = scsi_select_ctlr(DP8490); - while(SCSI_5380->scsi_dmstat & SC_IRQ_SET) { + if (NCR5380->ncr_dmstat & SC_IRQ_SET) { intr_disable(IR_SCSI1); - /* PID("&"); */ - if((itype = check_intr()) != INTR_SPURIOUS) { - /* PID("o"); */ - if(itype == INTR_RESEL) - reselect(); - else { -#ifdef REAL_DMA - if(!(dma_done = dma_ready(0, 0))) { - intr_enable(IR_SCSI1); - transfer_dma(connected, connected->phase, 0); - scsi_select_ctlr(ctrlr); - return; - } -#else - printf("would like to run transfer_dma\n"); -#endif - } - SC_CLINT; - } - /* PID("*"); */ softintr((int)softint); - scsi_select_ctlr(ctrlr); - return; } - PID("("); + scsi_select_ctlr(ctrlr); +} + +static void +ncr_soft_intr(sc) + void *sc; +{ + int ctrlr = scsi_select_ctlr(DP8490); + ncr_ctrl_intr(sc); scsi_select_ctlr(ctrlr); } /* - * Initiate a connection path between the host and the target. The function - * first goes into arbitration for the SCSI-bus. When this succeeds, the target - * is selected and an 'IDENTIFY' message is send. - * Returns -1 when the arbitration failed. Otherwise 0 is returned. When - * the target does not respond (to either selection or 'MESSAGE OUT') the - * 'done' function is executed. - * The result code given by the driver can be influenced by setting 'code' - * to a non-zero value. This is the case when 'select' is called by abort. + * PDMA stuff */ -static int -scsi_select(reqp, code) -SC_REQ *reqp; -{ - u_long timeout; - u_char tmp[1]; - u_char phase; - u_long cnt; - int sps; - - DBG_SELPRINT ("Starting arbitration\n", 0); - PID("T"); - - sps = splbio(); - - /* - * Prevent a race condition here. If a reslection interrupt occurred - * between the decision to pick a new request and the call to select, - * we abort the selection. - * Interrupts are lowered when the 5380 is setup to arbitrate for the - * bus. - */ - if(connected || (SCSI_5380->scsi_idstat & SC_S_BSY)) { - splx(sps); - PID("Y"); - return(-1); - } - - /* - * Set phase bits to 0, otherwise the 5380 won't drive the bus during - * selection. - */ - SCSI_5380->scsi_tcom = 0; - SCSI_5380->scsi_icom = 0; - - /* - * Arbitrate for the bus. - */ - SCSI_5380->scsi_data = SC_HOST_ID; - SCSI_5380->scsi_mode = SC_ARBIT; - - splx(sps); - - cnt = 10000; - while(!(SCSI_5380->scsi_icom & SC_AIP) && --cnt) - delay(1); - - if(!(SCSI_5380->scsi_icom & SC_AIP)) { - SCSI_5380->scsi_mode = IMODE_BASE; - delay(1); - PID("U"); - - if(SCSI_5380->scsi_idstat & SC_S_BSY) { - /* - * Damn, we have a connected target that we don't know - * of. Some targets seem to respond to a selection - * AFTER the selection-timeout. Try to get the target - * into the Message-out phase so we can send an ABORT - * message. We try to avoid resetting the SCSI-bus! - */ - if(!reach_msg_out(sizeof(struct scsi_generic))) { - u_long len = 1; - u_char phase = PH_MSGOUT; - u_char msg = MSG_ABORT; - - transfer_pio(&phase, &msg, &len); - } - else if(SCSI_5380->scsi_idstat & SC_S_BSY) - scsi_reset(); - } - PID("I"); - return(-1); - } - - /* The arbitration delay is 2.2 usecs */ - delay(3); - - /* - * Check the result of the arbitration. If we failed, return -1. - */ - if(SCSI_5380->scsi_icom & SC_LA) { - /* - * The spec requires that we should read the data register to - * check for higher id's and check the SC_LA again. - */ - tmp[0] = SCSI_5380->scsi_data; - if(SCSI_5380->scsi_icom & SC_LA) { - SCSI_5380->scsi_mode = IMODE_BASE; - SCSI_5380->scsi_icom = 0; - DBG_SELPRINT ("Arbitration lost,deassert SC_ARBIT\n",0); - PID("O"); - return(-1); - } - } - SCSI_5380->scsi_icom = SC_A_SEL | SC_A_BSY; - if(SCSI_5380->scsi_icom & SC_LA) { - SCSI_5380->scsi_mode = IMODE_BASE; - SCSI_5380->scsi_icom = 0; - DBG_SELPRINT ("Arbitration lost, deassert SC_A_SEL\n", 0); - PID("P"); - return(-1); - } - /* Bus settle delay + Bus clear delay = 1.2 usecs */ - delay(2); - DBG_SELPRINT ("Arbitration complete\n", 0); - - /* - * Now that we won the arbitration, start the selection. - */ - SCSI_5380->scsi_data = SC_HOST_ID | (1 << reqp->targ_id); - - /* - * Raise ATN while SEL is true before BSY goes false from arbitration, - * since this is the only way to guarantee that we'll get a MESSAGE OUT - * phase immediately after the selection. - */ - SCSI_5380->scsi_icom = SC_A_BSY | SC_A_SEL | SC_A_ATN | SC_ADTB; - SCSI_5380->scsi_mode = IMODE_BASE; - - /* - * Turn off reselection interrupts - */ - SCSI_5380->scsi_idstat = 0; - - /* - * Reset BSY. The delay following it, surpresses a glitch in the - * 5380 which causes us to see our own BSY signal instead of that of - * the target. - */ - SCSI_5380->scsi_icom = SC_A_SEL | SC_A_ATN | SC_ADTB; - delay(1); - - /* - * Wait for the target to react, the specs call for a timeout of - * 250 ms. - */ - cnt = 250000 /* 250 */; - while(!(SCSI_5380->scsi_idstat & SC_S_BSY) && --cnt) - delay(1); - - if(!(SCSI_5380->scsi_idstat & SC_S_BSY)) { - /* - * There is no reaction from the target, start the selection - * timeout procedure. We release the databus but keep SEL - * asserted. After that we wait a 'selection abort time' (200 - * usecs) and 2 deskew delays (90 ns) and check BSY again. - * When BSY is asserted, we assume the selection succeeded, - * otherwise we release the bus. - */ - SCSI_5380->scsi_icom = SC_A_SEL | SC_A_ATN; - delay(201); - if(!(SCSI_5380->scsi_idstat & SC_S_BSY)) { - SCSI_5380->scsi_icom = 0; - reqp->xs->error = code ? code : XS_SELTIMEOUT; - DBG_SELPRINT ("Target %d not responding to sel\n", - reqp->targ_id); - finish_req(reqp); - PID("A"); - return(0); - } - } - SCSI_5380->scsi_icom = SC_A_ATN; - - DBG_SELPRINT ("Target %d responding to select.\n", reqp->targ_id); - - /* - * The SCSI-interrupts are disabled while a request is being handled. - */ - intr_disable(IR_SCSI1); - - /* - * Since we followed the SCSI-spec and raised ATN while SEL was true - * but before BSY was false during the selection, a 'MESSAGE OUT' - * phase should follow. Here we send an 'IDENTIFY' message. - * Allow disconnect only when interrups are allowed. - */ - tmp[0] = MSG_IDENTIFY(reqp->targ_lun, (reqp->dr_flag & DRIVER_NOINT) ? 0 : 1); - cnt = 1; - phase = PH_MSGOUT; - if(transfer_pio(&phase, tmp, &cnt) || cnt) { - DBG_SELPRINT ("Target %d: failed to send identify\n", - reqp->targ_id); - /* - * Try to disconnect from the target. We cannot leave it just - * hanging here. - */ - if(!reach_msg_out(sizeof(struct scsi_generic))) { - u_long len = 1; - u_char phase = PH_MSGOUT; - u_char msg = MSG_ABORT; - - transfer_pio(&phase, &msg, &len); - } - else scsi_reset(); - - SCSI_5380->scsi_icom = 0; - reqp->xs->error = code ? code : XS_DRIVER_STUFFUP; - finish_req(reqp); - PID("S"); - return(0); - } - reqp->phase = PH_MSGOUT; - -#ifdef notyet /* LWP: Do we need timeouts in the driver? */ - /* - * Command is connected, start timer ticking. - */ - ccb_p->xtimeout = ccb_p->timeout + Lbolt; -#endif - - connected = reqp; - busy |= 1 << reqp->targ_id; - PID("D"); - return(0); -} - -/* - * Return codes: - * -1: quit main, trigger on interrupt - * 0: keep on running main. - */ -static int -information_transfer() -{ - SC_REQ *reqp = connected; - u_char tmp, phase; - u_long len; - - PID("F"); - /* - * Clear pending interrupts from 5380-chip. - */ - SC_CLINT; - - /* - * We only have a valid SCSI-phase when REQ is asserted. Something - * is deadly wrong when BSY has dropped. - */ - tmp = SCSI_5380->scsi_idstat; - - if(!(tmp & SC_S_BSY)) { - busy &= ~(1 << reqp->targ_id); - connected = NULL; - reqp->xs->error = XS_BUSY; - finish_req(reqp); - PID("G"); - return(0); - } - - if(tmp & SC_S_REQ) { - phase = (tmp >> 2) & 7; - if(phase != reqp->phase) { - reqp->phase = phase; - DBG_INFPRINT(show_phase, reqp, phase); - } - } - else return(-1); - - switch(phase) { - case PH_DATAOUT: - -#ifdef DBG_NOWRITE - printf("NOWRITE set -- write attempt aborted."); - reqp->msgout = MSG_ABORT; - SCSI_5380->scsi_icom = SC_A_ATN; - return(-1); -#endif /* DBG_NOWRITE */ - - case PH_DATAIN: -#ifdef REAL_DMA - if(reqp->dr_flag & DRIVER_DMAOK) { - int poll = REAL_DMA_POLL|(reqp->dr_flag & DRIVER_NOINT); - transfer_dma(reqp, phase, poll); - if(!poll) - return(0); - } - else -#endif - { - int s; - PID("H"); - len = reqp->xdata_len; - transfer_pdma(&phase, reqp->xdata_ptr, &len); - reqp->xdata_ptr += reqp->xdata_len - len; - reqp->xdata_len = len; - PID("h"); - } - return(-1); - case PH_MSGIN: - /* - * We only expect single byte messages here. - */ - len = 1; - transfer_pio(&phase, &tmp, &len); - reqp->message = tmp; - return(handle_message(reqp, tmp)); - case PH_MSGOUT: - len = 1; - transfer_pio(&phase, &reqp->msgout, &len); - if(reqp->msgout == MSG_ABORT) { - busy &= ~(1 << reqp->targ_id); - connected = NULL; - reqp->xs->error = XS_DRIVER_STUFFUP; - finish_req(reqp); - PID("J"); - return(0); - } - reqp->msgout = MSG_NOOP; - return(-1); - case PH_CMD : - len = command_size(reqp->xcmd.opcode); - transfer_pio(&phase, (u_char *)&reqp->xcmd, &len); - PID("K"); - return(-1); - case PH_STATUS: - len = 1; - transfer_pio(&phase, &tmp, &len); - reqp->status = tmp; - PID("L"); - return(-1); - default : - printf("ncr0: unknown phase on target %d\n", reqp->targ_id); - } - PID(":"); - return(-1); -} - -/* - * Handle the message 'msg' send to us by the target. - * Return values: - * 0 : The current command has completed. - * -1 : Get on to the next phase. - */ -static int -handle_message(reqp, msg) -SC_REQ *reqp; -u_int msg; -{ - int sps; - - PID(";"); - switch(msg) { - /* - * Linking lets us reduce the time required to get - * the next command to the device, skipping the arbitration - * and selection time. In the current implementation, - * we merely have to start the next command pointed - * to by 'next_link'. - */ - case MSG_LINK_CMD_COMPLETE: - case MSG_LINK_CMD_COMPLETEF: - if(reqp->link == NULL) { - printf("ncr0: no link for linked command" - "on target %d\n", reqp->targ_id); - reqp->msgout = MSG_ABORT; - SCSI_5380->scsi_icom = SC_A_ATN; - PID("@"); - return(-1); - } - reqp->xs->error = 0; - -#ifdef AUTO_SENSE - if(check_autosense(reqp, 0) == -1) - return(-1); -#endif /* AUTO_SENSE */ - -#ifdef DBG_REQ - show_request(reqp->link, "LINK"); -#endif - connected = reqp->link; - finish_req(reqp); - PID("'"); - return(-1); - case MSG_ABORT: - case MSG_CMDCOMPLETE: -#ifdef DBG_REQ - show_request(reqp, "DONE"); -#endif - connected = NULL; - busy &= ~(1 << reqp->targ_id); - if(!(reqp->dr_flag & DRIVER_AUTOSEN)) - reqp->xs->resid = reqp->xdata_len; - reqp->xs->error = 0; - -#ifdef AUTO_SENSE - if(check_autosense(reqp, 0) == -1) { - PID("Z"); - return(0); - } -#endif /* AUTO_SENSE */ - - finish_req(reqp); - PID("X"); - return(0); - case MSG_MESSAGE_REJECT: - PID("C"); - return(-1); - case MSG_DISCONNECT: -#ifdef DBG_REQ - show_request(reqp, "DISCON"); -#endif - PID("v"); - sps = splbio(); - connected = NULL; - reqp->next = discon_q; - discon_q = reqp; - splx(sps); - PID("V"); - return(0); - case MSG_SAVEDATAPOINTER: - case MSG_RESTOREPOINTERS: - /* - * We save pointers implicitely at disconnect. - * So we can ignore these messages. - */ - PID("B"); - return(-1); - default: - printf("ncr0: unkown message %x on target %d\n", msg, - reqp->targ_id); - return(-1); - } - PID("N"); - return(-1); -} - -/* - * Handle reselection. If a valid reconnection occurs, connected - * points at the reconnected command. The command is removed from the - * disconnected queue. - */ -static void -reselect() -{ - u_char phase; - u_long len; - u_char msg; - u_char target_mask; - int abort = 0, s; - SC_REQ *tmp, *prev; - - s = splbio(); - PID("M"); - target_mask = SCSI_5380->scsi_data & ~SC_HOST_ID; - /* - * At this point, we have detected that our SCSI-id is on the bus, - * SEL is true and BSY was false for at least one bus settle - * delay (400 ns.). - * We must assert BSY ourselves, until the target drops the SEL signal. - */ - SCSI_5380->scsi_icom = SC_A_BSY; - while(SCSI_5380->scsi_idstat & SC_S_SEL) - ; - - SCSI_5380->scsi_icom = 0; - - /* - * Get the expected identify message. - */ - phase = PH_MSGIN; - len = 1; - transfer_pio(&phase, &msg, &len); - if(len || !MSG_ISIDENTIFY(msg)) { - printf("ncr0: expecting IDENTIFY, got 0x%x\n", msg); - abort = 1; - } - else { - /* - * Find the command reconnecting - */ - for(tmp = discon_q, prev = NULL; tmp; prev = tmp, tmp = tmp->next){ - if(target_mask == (1 << tmp->targ_id)) { - if(prev) - prev->next = tmp->next; - else discon_q = tmp->next; - tmp->next = NULL; - break; - } - } - if(tmp == NULL) { - printf("ncr0: no disconnected job for targetmask %x\n", - target_mask); - abort = 1; - } - } - if(abort) { - msg = MSG_ABORT; - len = 1; - phase = PH_MSGOUT; - - SCSI_5380->scsi_icom = SC_A_ATN; - transfer_pio(&phase, &msg, &len); - } - else { - connected = tmp; -#ifdef DBG_REQ - show_request(tmp, "RECON"); -#endif - } - PID("<"); - splx(s); -} - -/* - * Transfer data in a given phase using polled I/O. - * Returns -1 when a different phase is entered without transferring the - * maximum number of bytes, 0 if all bytes or exit is in the same - * phase. - */ -static int -transfer_pio(phase, data, len) -u_char *phase; -u_char *data; -u_long *len; -{ - u_int cnt = *len; - u_char ph = *phase; - u_char tmp; - - DBG_PIOPRINT ("ncr0: transfer_pio start: phase: %d, len: %d\n", ph,cnt); - PID(","); - SCSI_5380->scsi_tcom = ph; - do { - if(!wait_req_true()) { - DBG_PIOPRINT ("ncr0: transfer_pio: missing REQ\n", 0, 0); - break; - } - if(((SCSI_5380->scsi_idstat >> 2) & 7) != ph) { - DBG_PIOPRINT ("ncr0: transfer_pio: phase mismatch\n", 0, 0); - break; - } - if(PH_IN(ph)) { - *data++ = SCSI_5380->scsi_data; - SCSI_5380->scsi_icom = SC_A_ACK; - } - else { - SCSI_5380->scsi_data = *data++; - - /* - * The SCSI-standard suggests that in the 'MESSAGE OUT' phase, - * the initiator should drop ATN on the last byte of the - * message phase after REQ has been asserted for the handshake - * but before the initiator raises ACK. - */ - if(!( (ph == PH_MSGOUT) && (cnt > 1) )) { - SCSI_5380->scsi_icom = SC_ADTB; - SCSI_5380->scsi_icom = SC_ADTB | SC_A_ACK; - } - else { - SCSI_5380->scsi_icom = SC_ADTB | SC_A_ATN; - SCSI_5380->scsi_icom = SC_ADTB | SC_A_ATN | SC_A_ACK; - } - } - if(!wait_req_false()) { - DBG_PIOPRINT ("ncr0: transfer_pio - REQ not dropping\n", 0, 0); - break; - } - - if(!( (ph == PH_MSGOUT) && (cnt > 1) )) - SCSI_5380->scsi_icom = 0; - else SCSI_5380->scsi_icom = SC_A_ATN; - } while(--cnt); - - if((tmp = SCSI_5380->scsi_idstat) & SC_S_REQ) - *phase = (tmp >> 2) & 7; - else *phase = NR_PHASE; - *len = cnt; - DBG_PIOPRINT ("ncr0: transfer_pio done: phase: %d, len: %d\n", - *phase, cnt); - PID(">"); - if(!cnt || (*phase == ph)) - return(0); - return(-1); -} - -#ifdef REAL_DMA -/* - * Start a DMA-transfer on the device using the current pointers. - * If 'poll' is true, the function waits until DMA-has completed. - */ -static void -transfer_dma(reqp, phase, poll) -SC_REQ *reqp; -u_int phase; -int poll; -{ - int dmastat; - u_char mbase = 0; - int sps; - -again: - PID("?"); - /* - * We should be in phase, otherwise we are not allowed to - * drive the bus. - */ - SCSI_5380->scsi_tcom = phase; - - /* - * Defer interrupts until DMA is fully running. - */ - sps = splbio(); - - /* - * Clear pending interrupts and parity errors. - */ - SCSI_DMA->s_dma_ctrl = 0; - SC_CLINT; - - if(!poll) { - /* - * Enable SCSI interrupts and set IN_DMA flag, set 'mbase' - * to the interrupts we want enabled. - */ - intr_enable(IR_SCSI1); - reqp->dr_flag |= DRIVER_IN_DMA; - mbase = SC_E_EOPI | SC_MON_BSY; - } - - if(PH_IN(phase)) { - SCSI_DMA->s_dma_ctrl = SD_IN; - set_scsi_dma(&(SCSI_DMA->s_dma_ptr), reqp->dm_cur->dm_addr); - set_scsi_dma(&(SCSI_DMA->s_dma_cnt), reqp->dm_cur->dm_count); - SCSI_5380->scsi_icom = 0; - SCSI_5380->scsi_mode = IMODE_BASE | mbase | SC_M_DMA; - SCSI_DMA->s_dma_ctrl = SD_ENABLE; - SCSI_5380->scsi_ircv = 0; - } - else { - SCSI_DMA->s_dma_ctrl = SD_OUT; - set_scsi_dma(&(SCSI_DMA->s_dma_ptr), reqp->dm_cur->dm_addr); - set_scsi_dma(&(SCSI_DMA->s_dma_cnt), reqp->dm_cur->dm_count); - SCSI_5380->scsi_mode = IMODE_BASE | mbase | SC_M_DMA; - SCSI_5380->scsi_icom = SC_ADTB; - SCSI_5380->scsi_dmstat = 0; - SCSI_DMA->s_dma_ctrl = SD_ENABLE|SD_OUT; - } - splx(sps); - - if(poll) { - /* - * We wait here until the DMA has finished. This can be - * achieved by checking the following conditions: - * - 5380: - * - End of DMA flag is set - * - We lost BSY (error!!) - * - A phase mismatch has occured (partial transfer) - * - DMA-controller: - * - A bus error occurred (Kernel error!!) - * - All bytes are transferred - * We one of the terminating conditions was met, we call - * 'dma_ready' to check errors and perform the bookkeeping. - */ - u_char dmstat, dmastat; - int dma_done; - - PID("/"); - for(;;) { - dmstat = SCSI_5380->scsi_dmstat; - dmastat = SCSI_DMA->s_dma_ctrl; - if(dmstat & (SC_END_DMA|SC_BSY_ERR|SC_IRQ_SET)) - break; - if(!(dmstat & SC_PHS_MTCH)) - break; - if(dmastat & (SD_BUSERR|SD_ZERO)) - break; - } - PID("q"); - if(dmastat & (SD_BUSERR|SD_ZERO)) - dma_done = dma_ready(1, dmastat); - else dma_done = dma_ready(0, 0); - if(!dma_done) - goto again; - - } - PID("w"); -} -#endif - -/* - * Check results of a DMA data-transfer. - */ -static int -dma_ready(has_dmastat, dmastat) -int has_dmastat, dmastat; -{ - int dmstat, phase; - long tmp, bytes_left, bytes_done; - int i; - SC_REQ *reqp = connected; - - /* - * Get values of the status registers of the 5380 & DMA-controller. - */ - dmstat = SCSI_5380->scsi_dmstat; -#ifdef REAL_DMA - if(!has_dmastat) - dmastat = SCSI_DMA->s_dma_ctrl; -#endif - - /* - * Check if the call is sensible and not caused by any spurious - * interrupt. - */ - if( -#ifdef REAL_DMA - !(dmastat & (SD_BUSERR|SD_ZERO)) && -#endif - !(dmstat & (SC_END_DMA|SC_BSY_ERR)) - && (dmstat & SC_PHS_MTCH) ) { - printf("ncr0: spurious call to dma_ready (dma:%x,dm:%x)\n", - dmastat, dmstat); - return(0); - } - -#ifdef REAL_DMA - /* - * If end of a DMA transfer, check it's results. - */ - get_scsi_dma(SCSI_DMA->s_dma_cnt, bytes_left); - - if(dmastat & SD_BUSERR) { - /* - * The DMA-controller seems to access 8 bytes beyond - * it's limits on output. Therefore check also the byte - * count. If it's zero, ignore the bus error. - */ - if(bytes_left != 0) { - get_scsi_dma(SCSI_DMA->s_dma_ptr, tmp); - printf("SCSI-DMA buserror - accessing 0x%x\n", tmp); - panic("SCSI"); - } - } - - if(bytes_left != 0) { - /* - * The byte-count is not zero. This is not always an error,on - * tape drives this usually means EOF. We check that the - * residu is a multiple of 512-bytes, when we know the device - * type it should be included in the check. - * A severe complication is that if a device wants to - * disconnect in the middle of a DMA-transfer, the 5380 has - * already prefetched the next byte from the DMA-controller - * before the phase mismatch occurs. I don't know how this - * fact can be uniquely identified. For the moment, we round - * up the residual if it is odd. - */ - if(PH_OUT(reqp->phase) && (bytes_left & 1)) - bytes_left++; - - } - else { - if(PH_IN(reqp->phase)) { - /* - * Check for bytes not transfered. This should never occur - * because the buffer-pool is aligned on a 4-byte boundary. - */ - u_char *p, *q; - - if(bytes_left > 3) { - scsi_show(); - printf("ncr0: %d bytes not transferred\n", bytes_left); - panic("ncr0"); - } - p = (u_char*)(bytes_left & ~3); - q = (u_char*)&(SCSI_DMA->s_dma_res); - switch(bytes_left & 3) { - case 3: *p++ = *q++; - case 2: *p++ = *q++; - case 1: *p++ = *q++; - printf("ncr0: dma residue count != 0, "); - printf("Check buffer alignment\n"); - } - } - } - - /* - * Update various transfer-pointers/lengths - */ - bytes_done = reqp->dm_cur->dm_count - bytes_left; - - reqp->xdata_ptr = &reqp->xdata_ptr[bytes_done]; /* XXX */ - reqp->xdata_len -= bytes_done; /* XXX */ - if((reqp->dm_cur->dm_count -= bytes_done) == 0) - reqp->dm_cur++; - else reqp->dm_cur->dm_addr += bytes_done; -#endif - - if(PH_IN(reqp->phase) && (dmstat & SC_PAR_ERR)) { - if(!(ncr5380_no_parchk & (1 << reqp->targ_id))) - /* XXX: Should be parity error ???? */ - reqp->xs->error = XS_DRIVER_STUFFUP; - } - - /* - * DMA mode should always be reset even when we will continue with the - * next chain. - */ - SCSI_5380->scsi_mode &= ~SC_M_DMA; - - if((dmstat & SC_BSY_ERR) || !(dmstat & SC_PHS_MTCH) -#ifdef REAL_DMA - || (reqp->dm_cur > reqp->dm_last) -#endif - ) { - /* - * Tell interrupt functions DMA mode has ended. - */ - reqp->dr_flag &= ~DRIVER_IN_DMA; - - /* - * Turn off DMA-mode and clear icom - */ - SCSI_5380->scsi_mode &= - ~(SC_M_DMA|SC_E_EOPI|SC_MON_BSY|SC_E_PARI); - SCSI_5380->scsi_icom = 0; - - /* - * Clear all (pending) interrupts. - */ -#ifdef REAL_DMA - SCSI_DMA->s_dma_ctrl = 0; -#endif - SC_CLINT; - - if(dmstat & SC_BSY_ERR) { - if(!reqp->xs->error) - reqp->xs->error = XS_BUSY; - finish_req(reqp); - PID("r"); - return(1); - } - - if(reqp->xs->error != 0) { -printf("dma-ready: code = %d\n", reqp->xs->error); /* LWP */ - reqp->msgout = MSG_ABORT; - SCSI_5380->scsi_icom = SC_A_ATN; - } - PID("t"); - return(1); - } - return(0); -} - -static int -check_autosense(reqp, linked) -SC_REQ *reqp; -int linked; -{ - int sps; - - /* - * If we not executing an auto-sense and the status code - * is request-sense, we automatically issue a request - * sense command. - */ - PID("y"); - if(!(reqp->dr_flag & DRIVER_AUTOSEN)) { - if(reqp->status == SCSCHKC) { - memcpy(&reqp->xcmd, sense_cmd, sizeof(sense_cmd)); - reqp->xdata_ptr = (u_char *)&reqp->xs->sense; - reqp->xdata_len = sizeof(reqp->xs->sense); - reqp->dr_flag |= DRIVER_AUTOSEN; - reqp->dr_flag &= ~DRIVER_DMAOK; - if(!linked) { - sps = splbio(); - reqp->next = issue_q; - issue_q = reqp; - splx(sps); - } - else reqp->xcmd.bytes[4] |= 1; - -#ifdef DBG_REQ - show_request(reqp, "AUTO-SENSE"); -#endif - PID("u"); - return(-1); - } - } - else { - /* - * An auto-sense has finished - */ - if((reqp->status & SCSMASK) != SCSGOOD) - reqp->xs->error = XS_DRIVER_STUFFUP; /* SC_E_AUTOSEN; */ - else reqp->xs->error = XS_SENSE; - reqp->status = SCSCHKC; - } - PID("i"); - return(0); -} - -static int -reach_msg_out(len) -u_long len; -{ - u_char phase; - u_char data; - - printf("ncr0: Trying to reach Message-out phase\n"); - if((phase = SCSI_5380->scsi_idstat) & SC_S_REQ) - phase = (phase >> 2) & 7; - else return(-1); - printf("ncr0: Trying to reach Message-out phase, now: %d\n", phase); - if(phase == PH_MSGOUT) - return(0); - - SCSI_5380->scsi_tcom = phase; - - do { - if(!wait_req_true()) { - break; - } - if(((SCSI_5380->scsi_idstat >> 2) & 7) != phase) { - break; - } - if(PH_IN(phase)) { - data = SCSI_5380->scsi_data; - SCSI_5380->scsi_icom = SC_A_ACK | SC_A_ATN; - } - else { - SCSI_5380->scsi_data = 0; - SCSI_5380->scsi_icom = SC_ADTB | SC_A_ATN | SC_A_ACK; - } - if(!wait_req_false()) { - break; - } - SCSI_5380->scsi_icom = SC_A_ATN; - } while(--len); - - if((phase = SCSI_5380->scsi_idstat) & SC_S_REQ) { - phase = (phase >> 2) & 7; - if(phase == PH_MSGOUT) { - printf("ncr0: Message-out phase reached.\n"); - return(0); - } - } - return(-1); -} - -static void -scsi_reset() -{ - SC_REQ *tmp, *next; - int sps; - - printf("ncr0: resetting SCSI-bus\n"); - - PID("7"); - sps = splbio(); - SCSI_5380->scsi_icom = SC_A_RST; - delay(1); - SCSI_5380->scsi_icom = 0; - - /* - * None of the jobs in the discon_q will ever be reconnected, - * notify this to the higher level code. - */ - for(tmp = discon_q; tmp ;) { - next = tmp->next; - tmp->next = NULL; - tmp->xs->error = XS_TIMEOUT; - busy &= ~(1 << tmp->targ_id); - finish_req(tmp); - tmp = next; - } - discon_q = NULL; - - /* - * The current job will never finish either. - * The problem is that we can't finish the job because an instance - * of main is running on it. Our best guess is that the job is currently - * doing REAL-DMA. In that case 'dma_ready()' should correctly finish - * the job because it detects BSY-loss. - */ - if(tmp = connected) { - if(tmp->dr_flag & DRIVER_IN_DMA) { - tmp->xs->error = XS_DRIVER_STUFFUP; - dma_ready(0, 0); - } - } - splx(sps); - PID("8"); -} - -/* - * Check validity of the IRQ set by the 5380. If the interrupt is valid, - * the appropriate action is carried out (reselection or DMA ready) and - * INTR_RESEL or INTR_DMA is returned. Otherwise a console notice is written - * and INTR_SPURIOUS is returned. - */ -static int -check_intr() -{ - SC_REQ *reqp; - - if((SCSI_5380->scsi_idstat & (SC_S_SEL|SC_S_IO))==(SC_S_SEL|SC_S_IO)) - return(INTR_RESEL); - else { - if((reqp = connected) && (reqp->dr_flag & DRIVER_IN_DMA)){ - reqp->dr_flag &= ~DRIVER_IN_DMA; - return(INTR_DMA); - } - } - SC_CLINT; - printf("-->"); - scsi_show(); - printf("ncr0: spurious interrupt.\n"); - return(INTR_SPURIOUS); -} - -#ifdef REAL_DMA -/* - * Check if DMA can be used for this request. This function also builds - * the dma-chain. - */ -static int -scsi_dmaok(reqp) -SC_REQ *reqp; -{ - u_long phy_buf; - u_long phy_len; - void *req_addr; - u_long req_len; - struct dma_chain *dm; - - /* - * Initialize locals and requests' DMA-chain. - */ - req_len = reqp->xdata_len; - req_addr = (void*)reqp->xdata_ptr; - dm = reqp->dm_cur = reqp->dm_last = reqp->dm_chain; - - dm->dm_count = dm->dm_addr = 0; - - /* - * Do not accept zero length DMA. - */ - if(req_len == 0) - return(0); - - /* - * LWP: I think that this restriction is not strictly nessecary. - */ - if((req_len & 0x1) || ((u_int)req_addr & 0x3)) - return(0); - - /* - * Build the DMA-chain. - */ - dm->dm_addr = phy_buf = kvtop(req_addr); - while(req_len) { - if(req_len < (phy_len = NBPG - ((u_long)req_addr & PGOFSET))) - phy_len = req_len; - - req_addr += phy_len; - req_len -= phy_len; - dm->dm_count += phy_len; - - if(req_len) { - u_long tmp = kvtop(req_addr); - - if((phy_buf + phy_len) != tmp) { - if(++dm >= &reqp->dm_chain[MAXDMAIO]) { - printf("ncr5380_dmaok: DMA chain too long!\n"); - return(0); - } - dm->dm_count = 0; - dm->dm_addr = tmp; - } - phy_buf = tmp; - } - } - reqp->dm_last = dm; - return(1); -} -#endif - -static void -run_main(void) -{ - int sps = splbio(); - - if(!main_running) { - main_running = 1; - splx(sps); - scsi_main(); - } else - splx(sps); -} - -/**************************************************************************** - * Start Debugging Functions * - ****************************************************************************/ -static void -show_data_sense(xs) -struct scsi_xfer *xs; -{ - u_char *b; - int i; - int sz; - - b = (u_char *) xs->cmd; - printf("cmd[%d,%d]: ", xs->cmdlen, sz = command_size(*b)); - for(i = 0; i < sz; i++) - printf("%x ", b[i]); - printf("\nsense: "); - b = (u_char *)&xs->sense; - for(i = 0; i < sizeof(xs->sense); i++) - printf("%x ", b[i]); - printf("\n"); -} - -static void -show_request(reqp, qtxt) -SC_REQ *reqp; -char *qtxt; -{ - printf("REQ-%s: %d %x[%d] cmd[0]=%x S=%x M=%x R=%x %s\n", qtxt, - reqp->targ_id, reqp->xdata_ptr, reqp->xdata_len, - reqp->xcmd.opcode, reqp->status, reqp->message, - reqp->xs->error, reqp->link ? "L" : ""); - if(reqp->status == SCSCHKC) - show_data_sense(reqp->xs); -} - -scsi_show() -{ - SC_REQ *tmp; - int sps = splhigh(); - -#ifndef DBG_PID - #define last_hit "" -#endif - - for(tmp = issue_q; tmp; tmp = tmp->next) - show_request(tmp, "ISSUED"); - for(tmp = discon_q; tmp; tmp = tmp->next) - show_request(tmp, "DISCONNECTED"); - if(connected) - show_request(connected, "CONNECTED"); - /* show_signals(); */ - if(connected) - printf("phase = %d, ", connected->phase); - printf("busy:%x, last_hit:%s, spl:%x dis:%x\n", busy, last_hit, sps, idisabled); - - splx(sps); -} - -#define TIMEOUT 100000 -#define READY(dataout) \ - i = TIMEOUT; \ - while (/* (SCSI_5380->scsi_dmstat & (SC_DMA_REQ|SC_PHS_MTCH)) != (SC_DMA_REQ|SC_PHS_MTCH) || \ - (SCSI_5380->scsi_dmstat & (SC_DMA_REQ|SC_PHS_MTCH)) != (SC_DMA_REQ|SC_PHS_MTCH) || */ \ - (SCSI_5380->scsi_dmstat & (SC_DMA_REQ|SC_PHS_MTCH)) != (SC_DMA_REQ|SC_PHS_MTCH)) \ - if ( !(SCSI_5380->scsi_dmstat & SC_PHS_MTCH) \ - || !(SCSI_5380->scsi_idstat & SC_S_BSY) \ - || (i-- < 0) ) { \ - if (i < 0 || ((*count - len) & 0x1ff)) printf("ncr0: timeout counter = %d, len = %d, count=%d, I/O = %c\n", \ - i, len, *count, *count - len, dataout ? 'O' : 'I'); \ - if (dataout) { \ - SCSI_5380->scsi_icom &= ~SC_ADTB; \ +#define load_tbl(a) do { \ + u_long *p = (u_long *) a; \ + *(p + 63) = *p; \ + } while (0) + +#define movsd(from, to, n) do { \ + register int r0 __asm ("r0") = n; \ + register u_char *r1 __asm("r1") = from; \ + register u_char *r2 __asm("r2") = to; \ + __asm volatile ("movsd" \ + : "=r" (r1), "=r" (r2) \ + : "0" (r1), "1" (r2), "r" (r0) \ + : "r0", "memory" \ + ); \ + from = r1; to = r2; \ + } while (0) + +#define movsb(from, to, n) do { \ + register int r0 __asm ("r0") = n; \ + register u_char *r1 __asm("r1") = from; \ + register u_char *r2 __asm("r2") = to; \ + __asm volatile ("movsb" \ + : "=r" (r1), "=r" (r2) \ + : "0" (r1), "1" (r2), "r" (r0) \ + : "r0", "memory" \ + ); \ + from = r1; to = r2; \ + } while (0) + +#define TIMEOUT 1000000 +#define READY(dataout) do { \ + for (i = TIMEOUT; i > 0; i --) { \ + if (NCR5380->ncr_dmstat & SC_IRQ_SET) { \ + if (dataout) NCR5380->ncr_icom &= ~SC_ADTB; \ + NCR5380->ncr_mode &= ~SC_M_DMA; \ + *count = len; \ + if ((idstat = NCR5380->ncr_idstat) & SC_S_REQ) \ + *phase = (idstat >> 2) & 7; \ + else \ + *phase = NR_PHASE; \ + return; \ } \ - SCSI_5380->scsi_mode &= ~SC_M_DMA; \ - *count = len; \ - if((idstat = SCSI_5380->scsi_idstat) & SC_S_REQ) \ - *phase = (idstat >> 2) & 7; \ - else \ - *phase = NR_PHASE; \ - return; \ - } - -#define WAIT_FOR_BSY do { \ - int scsi_timeout = TIMEOUT; \ - while (((SCSI_5380->scsi_idstat & SC_S_BSY) == 0) && \ - ((SCSI_5380->scsi_idstat & SC_S_BSY) == 0) && \ - ((SCSI_5380->scsi_idstat & SC_S_BSY) == 0) && \ - (--scsi_timeout) ); \ - if (!scsi_timeout) { \ - printf("ncr0: scsi timeout (WAIT_FOR_BSY)\n"); \ - goto scsi_timeout_error; \ - } \ - } while (0) + if (NCR5380->ncr_dmstat & SC_DMA_REQ) break; \ + delay(1); \ + } \ + if (i <= 0) panic("ncr0: pdma timeout"); \ + } while (0) #define byte_data ((volatile u_char *)pdma) #define word_data ((volatile u_short *)pdma) @@ -1899,10 +204,12 @@ scsi_show() #define W4(n) *long_data = *((u_long *)data + n) #define R1(n) *(data + n) = *byte_data #define R4(n) *((u_long *)data + n) = *long_data -#define BURST 8 static void -transfer_pdma(u_char *phase, u_char *data, u_long *count) +transfer_pdma(phase, data, count) + u_char *phase; + u_char *data; + u_long *count; { register volatile u_char *pdma = PDMA_ADDRESS; register int len = *count, i, idstat; @@ -1911,42 +218,36 @@ transfer_pdma(u_char *phase, u_char *data, u_long *count) transfer_pio(phase, data, count); return; } - SCSI_5380->scsi_tcom = *phase; - WAIT_FOR_BSY; + NCR5380->ncr_tcom = *phase; + scsi_clr_ipend(); if (PH_IN(*phase)) { - SCSI_5380->scsi_icom = 0; - SCSI_5380->scsi_mode = IMODE_BASE | SC_M_DMA; - SCSI_5380->scsi_ircv = 0; - while (len >= 64 * BURST) { - delay(10); + NCR5380->ncr_icom = 0; + NCR5380->ncr_mode = IMODE_BASE | SC_M_DMA; + NCR5380->ncr_ircv = 0; + while (len >= 256) { READY(0); -#if (BURST != 1) - i = BURST; di(); - do { -#endif - R4( 0); R4( 1); R4( 2); R4( 3); - R4( 4); R4( 5); R4( 6); R4( 7); - R4( 8); R4( 9); R4(10); R4(11); - R4(12); R4(13); R4(14); R4(15); - data += 64; -#if (BURST != 1) - } while (--i != 0); -#endif + load_tbl(data); + di(); /* Serialize CPU */ + movsd((u_char *)pdma, data, 64); ei(); - len -= 64 * BURST; + len -= 256; } - while (len) { - READY(0); - R1(0); - data++; - len--; + if (len) { + di(); + while (len) { + READY(0); + R1(0); + data++; + len--; + } + ei(); } } else { - SCSI_5380->scsi_mode = IMODE_BASE | SC_M_DMA; - SCSI_5380->scsi_icom = SC_ADTB; - SCSI_5380->scsi_dmstat = SC_S_SEND; - while (len >= 64 * BURST) { + NCR5380->ncr_mode = IMODE_BASE | SC_M_DMA; + NCR5380->ncr_icom = SC_ADTB; + NCR5380->ncr_dmstat = SC_S_SEND; + while (len >= 256) { /* The second ready is to * compensate for DMA-prefetch. * Since we adjust len only at @@ -1954,47 +255,44 @@ transfer_pdma(u_char *phase, u_char *data, u_long *count) * is no need to correct the * residue. */ - READY(1); W1(0); READY(1); W1(1); W2(1); -#if (BURST != 1) - i = BURST; - goto skip0; - do { -#endif - W4( 0); - skip0: - W4( 1); W4( 2); W4( 3); - W4( 4); W4( 5); W4( 6); W4( 7); - W4( 8); W4( 9); W4(10); W4(11); - W4(12); W4(13); W4(14); W4(15); - data += 64; -#if (BURST != 1) - } while (--i != 0); -#endif - len -= 64 * BURST; + READY(1); + di(); + W1(0); READY(1); W1(1); W2(1); + data += 4; + movsd(data, (u_char *)pdma, 63); + ei(); + len -= 256; } if (len) { READY(1); + di(); while (len) { W1(0); READY(1); data++; len--; } + ei(); } i = TIMEOUT; - while (((SCSI_5380->scsi_dmstat & (SC_DMA_REQ|SC_PHS_MTCH)) + while (((NCR5380->ncr_dmstat & (SC_DMA_REQ|SC_PHS_MTCH)) == SC_PHS_MTCH) && --i); if (!i) printf("ncr0: timeout waiting for SC_DMA_REQ.\n"); *byte_data = 0; } -scsi_timeout_error: - SCSI_5380->scsi_mode &= ~SC_M_DMA; - if((idstat = SCSI_5380->scsi_idstat) & SC_S_REQ) +ncr_timeout_error: + NCR5380->ncr_mode &= ~SC_M_DMA; + if((idstat = NCR5380->ncr_idstat) & SC_S_REQ) *phase = (idstat >> 2) & 7; else *phase = NR_PHASE; *count = len; return; } + +/* + * Last but not least... Include the general driver code + */ +#include "ncr5380.c" diff --git a/sys/arch/pc532/dev/ncr5380.c b/sys/arch/pc532/dev/ncr5380.c new file mode 100644 index 000000000000..686557b7605d --- /dev/null +++ b/sys/arch/pc532/dev/ncr5380.c @@ -0,0 +1,1721 @@ +/* $NetBSD: ncr5380.c,v 1.1 1995/08/25 07:30:35 phil Exp $ */ + +/* + * Copyright (c) 1995 Leo Weppelman. + * 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 Leo Weppelman. + * 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. + */ + + +#ifdef DBG_NOSTATIC +# define static +#endif +#ifdef DBG_SEL +# define DBG_SELPRINT(a,b) printf(a,b) +#else +# define DBG_SELPRINT(a,b) +#endif +#ifdef DBG_PIO +# define DBG_PIOPRINT(a,b,c) printf(a,b,c) +#else +# define DBG_PIOPRINT(a,b,c) +#endif +#ifdef DBG_INF +# define DBG_INFPRINT(a,b,c) a(b,c) +#else +# define DBG_INFPRINT(a,b,c) +#endif +#ifdef DBG_PID + static char *last_hit = NULL; +# define PID(a) last_hit = a +#else +# define PID(a) +#endif + + +/* + * Set bit for target when parity checking must be disabled. + * My (LWP) Maxtor 7245S seems to generate parity errors on about 50% + * of all transfers while the data is correct!? + */ +u_char ncr5380_no_parchk = 0xff; + +/* + * This is the default sense-command we send. + */ +static u_char sense_cmd[] = { + REQUEST_SENSE, 0, 0, 0, sizeof(struct scsi_sense), 0 +}; + +/* + * True if the main co-routine is running + */ +static volatile int main_running = 0; + +/* + * Mask of targets selected + */ +static u_char busy; + +static u_int ncr5380_minphys(struct buf *bp); +static int ncr5380_scsi_cmd(struct scsi_xfer *xs); +static int ncr5380_show_scsi_cmd(struct scsi_xfer *xs); + +struct scsi_adapter ncr5380_switch = { + ncr5380_scsi_cmd, /* scsi_cmd() */ + ncr5380_minphys, /* scsi_minphys() */ + 0, /* open_target_lu() */ + 0 /* close_target_lu() */ +}; + +struct scsi_device ncr5380_dev = { + NULL, /* use default error handler */ + NULL, /* do not have a start functio */ + NULL, /* have no async handler */ + NULL /* Use default done routine */ +}; + + +static SC_REQ req_queue[NREQ]; +static SC_REQ *free_head = NULL; /* Free request structures */ + + +/* + * Inline functions: + */ + +/* + * Determine the size of a SCSI command. + */ +extern __inline__ int command_size(opcode) +u_char opcode; +{ + switch ((opcode >> 4) & 0xf) { + case 0: + case 1: + return (6); + case 2: + case 3: + return (10); + } + return (12); +} + + +/* + * Wait for request-line to become active. When it doesn't return 0. + * Otherwise return != 0. + * The timeouts in the 'wait_req_*' functions are arbitrary and rather + * large. In 99% of the invocations nearly no timeout is needed but in + * some cases (especially when using my tapedrive, a Tandberg 3600) the + * device is busy internally and the first SCSI-phase will be delayed. + */ +extern __inline__ int wait_req_true(void) +{ + int timeout = 25000; + + while (!(GET_5380_REG(NCR5380_IDSTAT) & SC_S_REQ) && --timeout) + delay(1); + return (GET_5380_REG(NCR5380_IDSTAT) & SC_S_REQ); +} + +/* + * Wait for request-line to become inactive. When it doesn't return 0. + * Otherwise return != 0. + */ +extern __inline__ int wait_req_false(void) +{ + int timeout = 25000; + + while ((GET_5380_REG(NCR5380_IDSTAT) & SC_S_REQ) && --timeout) + delay(1); + return (!(GET_5380_REG(NCR5380_IDSTAT) & SC_S_REQ)); +} + +extern __inline__ void finish_req(SC_REQ *reqp) +{ + int sps; + struct scsi_xfer *xs = reqp->xs; + + /* + * Return request to free-q + */ + sps = splbio(); + reqp->next = free_head; + free_head = reqp; + splx(sps); + + xs->flags |= ITSDONE; + scsi_done(xs); +} + +/* + * Auto config stuff.... + */ +int ncr_cprint __P((void *auxp, char *)); +void ncr_attach __P((struct device *, struct device *, void *)); +int ncr_match __P((struct device *, struct cfdata *, void *)); + +/* + * Tricks to make driver-name configurable + */ +#define CFNAME(n) __CONCAT(n,cd) +#define CFSTRING(n) __STRING(n) + +struct cfdriver CFNAME(DRNAME) = { + NULL, CFSTRING(DRNAME), (cfmatch_t)ncr_match, ncr_attach, + DV_DULL, sizeof(struct ncr_softc), NULL, 0 }; + +int +ncr_match(pdp, cdp, auxp) +struct device *pdp; +struct cfdata *cdp; +void *auxp; +{ + return (machine_match(pdp, cdp, auxp, &CFNAME(DRNAME))); +} + +void +ncr_attach(pdp, dp, auxp) +struct device *pdp, *dp; +void *auxp; +{ + struct ncr_softc *sc; + int i; + + sc = (struct ncr_softc *)dp; + + sc->sc_link.adapter_softc = sc; + sc->sc_link.adapter_target = 7; + sc->sc_link.adapter = &ncr5380_switch; + sc->sc_link.device = &ncr5380_dev; + sc->sc_link.openings = NREQ - 1; + + /* + * Initialize machine-type specific things... + */ + scsi_mach_init(sc); + printf("\n"); + + /* + * Initialize request queue freelist. + */ + for (i = 0; i < NREQ; i++) { + req_queue[i].next = free_head; + free_head = &req_queue[i]; + } + + /* + * Initialize the host adapter + */ + scsi_idisable(); + ENABLE_NCR5380(sc); + SET_5380_REG(NCR5380_ICOM, 0); + SET_5380_REG(NCR5380_MODE, IMODE_BASE); + SET_5380_REG(NCR5380_TCOM, 0); + SET_5380_REG(NCR5380_IDSTAT, 0); + scsi_ienable(); + + /* + * attach all scsi units on us + */ + config_found(dp, &sc->sc_link, ncr_cprint); +} + +/* + * print diag if name is NULL else just extra + */ +int +ncr_cprint(auxp, name) +void *auxp; +char *name; +{ + if (name == NULL) + return (UNCONF); + return (QUIET); +} +/* + * End of auto config stuff.... + */ + +/* + * Carry out a request from the high level driver. + */ +static int +ncr5380_scsi_cmd(struct scsi_xfer *xs) +{ + int sps; + SC_REQ *reqp; + int flags = xs->flags; + + /* + * We do not queue RESET commands + */ + if (flags & SCSI_RESET) { + scsi_reset(xs->sc_link->adapter_softc); + return (COMPLETE); + } + + /* + * Get a request block + */ + sps = splbio(); + if ((reqp = free_head) == 0) { + splx(sps); + return (TRY_AGAIN_LATER); + } + free_head = reqp->next; + reqp->next = NULL; + splx(sps); + + /* + * Initialize our private fields + */ + reqp->dr_flag = (flags & SCSI_POLL) ? DRIVER_NOINT : 0; + reqp->phase = NR_PHASE; + reqp->msgout = MSG_NOOP; + reqp->status = SCSGOOD; + reqp->link = NULL; + reqp->xs = xs; + reqp->targ_id = xs->sc_link->target; + reqp->targ_lun = xs->sc_link->lun; + reqp->xdata_ptr = (u_char*)xs->data; + reqp->xdata_len = xs->datalen; + memcpy(&reqp->xcmd, xs->cmd, sizeof(struct scsi_generic)); + reqp->xcmd.bytes[0] |= reqp->targ_lun << 5; + + /* + * Sanity check on flags... + */ + if (flags & ITSDONE) { + ncr_tprint(reqp, "scsi_cmd: command already done.....\n"); + xs->flags &= ~ITSDONE; + } + if (!(flags & INUSE)) { + ncr_tprint(reqp, "scsi_cmd: command not in use.....\n"); + xs->flags |= ~INUSE; + } + + /* + * Check if DMA can be used on this request + */ + if (scsi_dmaok(reqp)) + reqp->dr_flag |= DRIVER_DMAOK; + + /* + * Insert the command into the issue queue. Note that 'REQUEST SENSE' + * commands are inserted at the head of the queue since any command + * will clear the existing contingent allegience condition and the sense + * data is only valid while the condition exists. + * When possible, link the command to a previous command to the same + * target. This is not very sensible when AUTO_SENSE is not defined! + * Interrupts are disabled while we are fiddling with the issue-queue. + */ + sps = splbio(); + if ((issue_q == NULL) || (reqp->xcmd.opcode == REQUEST_SENSE)) { + reqp->next = issue_q; + issue_q = reqp; + } + else { + SC_REQ *tmp, *link; + + tmp = issue_q; + link = NULL; + do { + if (!link && (tmp->targ_id == reqp->targ_id) && !tmp->link) + link = tmp; + } while (tmp->next && (tmp = tmp->next)); + tmp->next = reqp; +#ifdef AUTO_SENSE + if (link) { + link->link = reqp; + link->xcmd.bytes[link->xs->cmdlen-1] |= 1; + } +#endif + } + splx(sps); + +#ifdef DBG_REQ + show_request(reqp,(reqp->xcmd.opcode == REQUEST_SENSE) ? "HEAD":"TAIL"); +#endif + + run_main(xs->sc_link->adapter_softc); + + if (xs->flags & SCSI_POLL) + return (COMPLETE); /* We're booting */ + return (SUCCESSFULLY_QUEUED); +} + +#define MIN_PHYS 65536 /*BARF!!!!*/ +static u_int +ncr5380_minphys(struct buf *bp) +{ + if (bp->b_bcount > MIN_PHYS) { + printf("Uh-oh... ncr5380_minphys setting bp->b_bcount=%x.\n",MIN_PHYS); + bp->b_bcount = MIN_PHYS; + } + return (minphys(bp)); +} +#undef MIN_PHYS + +static int +ncr5380_show_scsi_cmd(struct scsi_xfer *xs) +{ + u_char *b = (u_char *) xs->cmd; + int i = 0; + + if (!(xs->flags & SCSI_RESET)) { + printf("(%d:%d:%d,0x%x)-", xs->sc_link->scsibus, + xs->sc_link->target, xs->sc_link->lun, xs->sc_link->flags); + while (i < xs->cmdlen) { + if (i) + printf(","); + printf("%x",b[i++]); + } + printf("-\n"); + } + else { + printf("(%d:%d:%d)-RESET-\n", + xs->sc_link->scsibus,xs->sc_link->target, xs->sc_link->lun); + } +} + +/* + * The body of the driver. + */ +static void +scsi_main(sc) +struct ncr_softc *sc; +{ + SC_REQ *req, *prev; + int itype; + int sps; + + /* + * While running in the driver SCSI-interrupts are disabled. + */ + scsi_idisable(); + ENABLE_NCR5380(sc); + + PID("scsi_main1"); + for (;;) { + sps = splbio(); + if (!connected) { + /* + * Check if it is fair keep any exclusive access to DMA + * claimed. If not, stop queueing new jobs so the discon_q + * will be eventually drained and DMA can be given up. + */ + if (!fair_to_keep_dma()) + goto main_exit; + + /* + * Search through the issue-queue for a command + * destined for a target that isn't busy. + */ + prev = NULL; + for (req=issue_q; req != NULL; prev = req, req = req->next) { + if (!(busy & (1 << req->targ_id))) { + /* + * Found one, remove it from the issue queue + */ + if (prev == NULL) + issue_q = req->next; + else prev->next = req->next; + req->next = NULL; + break; + } + } + + /* + * When a request has just ended, we get here before an other + * device detects that the bus is free and that it can + * reconnect. The problem is that when this happens, we always + * baffle the device because our (initiator) id is higher. This + * can cause a sort of starvation on slow devices. So we check + * for a pending reselection here. + * Note that 'connected' will be non-null if the reselection + * succeeds. + */ + if ((GET_5380_REG(NCR5380_IDSTAT) & (SC_S_SEL|SC_S_IO)) + == (SC_S_SEL|SC_S_IO)){ + if (req != NULL) { + req->next = issue_q; + issue_q = req; + } + splx(sps); + + reselect(sc); + scsi_clr_ipend(); + goto connected; + } + + /* + * The host is not connected and there is no request + * pending, exit. + */ + if (req == NULL) { + PID("scsi_main2"); + goto main_exit; + } + + /* + * Re-enable interrupts before handling the request. + */ + splx(sps); + +#ifdef DBG_REQ + show_request(req, "TARGET"); +#endif + /* + * We found a request. Try to connect to the target. If the + * initiator fails arbitration, the command is put back in the + * issue queue. + */ + if (scsi_select(req, 0)) { + sps = splbio(); + req->next = issue_q; + issue_q = req; + splx(sps); +#ifdef DBG_REQ + ncr_tprint(reqp, "Select failed\n"); +#endif + } + } + else splx(sps); +connected: + if (connected) { + /* + * If the host is currently connected but a 'real-dma' transfer + * is in progress, the 'end-of-dma' interrupt restarts main. + * So quit. + */ + sps = splbio(); + if (connected && (connected->dr_flag & DRIVER_IN_DMA)) { + PID("scsi_main3"); + goto main_exit; + } + splx(sps); + + /* + * Let the target guide us through the bus-phases + */ + while (information_transfer() == -1) + ; + } + } + /* NEVER TO REACH HERE */ + panic("ncr5380-SCSI: not designed to come here"); + +main_exit: + /* + * We enter here with interrupts disabled. We are about to exit main + * so interrupts should be re-enabled. Because interrupts are edge + * triggered, we could already have missed the interrupt. Therefore + * we check the IRQ-line here and re-enter when we really missed a + * valid interrupt. + */ + PID("scsi_main4"); + scsi_ienable(); + SET_5380_REG(NCR5380_IDSTAT, SC_HOST_ID); + if (GET_5380_REG(NCR5380_DMSTAT) & SC_IRQ_SET) { + if ((itype = check_intr(sc)) != INTR_SPURIOUS) { + scsi_idisable(); + splx(sps); + + if (itype == INTR_RESEL) + reselect(sc); +#ifdef REAL_DMA + else dma_ready(); +#else + else panic("Got DMA interrupt without DMA"); +#endif + scsi_clr_ipend(); + goto connected; + } + } + reconsider_dma(); + + main_running = 0; + splx(sps); + PID("scsi_main5"); +} + +#ifdef REAL_DMA +/* + * The SCSI-DMA interrupt. + * This interrupt can only be triggered when running in non-polled DMA + * mode. When DMA is not active, it will be silently ignored, it is usually + * to late because the EOP interrupt of the controller happens just a tiny + * bit earlier. It might become usefull when scatter/gather is implemented, + * because in that case only part of the DATAIN/DATAOUT transfer is taken + * out of a single buffer. + */ +static void +ncr_dma_intr(sc) +struct ncr_softc *sc; +{ + SC_REQ *reqp; + int dma_done; + + PID("ncr_dma_intr"); + if ((reqp = connected) && (reqp->dr_flag & DRIVER_IN_DMA)) { + scsi_idisable(); + if (!(dma_done = dma_ready())) { + transfer_dma(reqp, reqp->phase, 0); + return; + } + run_main(sc); + } +} +#endif /* REAL_DMA */ + +/* + * The SCSI-controller interrupt. This interrupt occurs on reselections and + * at the end of non-polled DMA-interrupts. It is assumed to be called from + * the machine-dependent hardware interrupt. + */ +static void +ncr_ctrl_intr(sc) +struct ncr_softc *sc; +{ + int itype; + int dma_done; + + while (GET_5380_REG(NCR5380_DMSTAT) & SC_IRQ_SET) { + scsi_idisable(); + if ((itype = check_intr(sc)) != INTR_SPURIOUS) { + if (itype == INTR_RESEL) + reselect(sc); + else { +#ifdef REAL_DMA + if (!(dma_done = dma_ready())) { + transfer_dma(connected, connected->phase, 0); + return; + } +#else + panic("Got DMA interrupt without DMA\n"); +#endif + } + scsi_clr_ipend(); + } + run_main(sc); + return; + } + PID("ncr_ctrl_intr1"); +} + +/* + * Initiate a connection path between the host and the target. The function + * first goes into arbitration for the SCSI-bus. When this succeeds, the target + * is selected and an 'IDENTIFY' message is send. + * Returns -1 when the arbitration failed. Otherwise 0 is returned. When + * the target does not respond (to either selection or 'MESSAGE OUT') the + * 'done' function is executed. + * The result code given by the driver can be influenced by setting 'code' + * to a non-zero value. This is the case when 'select' is called by abort. + */ +static int +scsi_select(reqp, code) +SC_REQ *reqp; +{ + u_long timeout; + u_char tmp[1]; + u_char phase; + u_long cnt; + int sps; + struct ncr_softc *sc; + + sc = reqp->xs->sc_link->adapter_softc; + DBG_SELPRINT ("Starting arbitration\n", 0); + PID("scsi_select1"); + + sps = splbio(); + + /* + * Prevent a race condition here. If a reslection interrupt occurred + * between the decision to pick a new request and the call to select, + * we abort the selection. + * Interrupts are lowered when the 5380 is setup to arbitrate for the + * bus. + */ + if (connected || (GET_5380_REG(NCR5380_IDSTAT) & SC_S_BSY)) { + splx(sps); + PID("scsi_select2"); + return (-1); + } + + /* + * Set phase bits to 0, otherwise the 5380 won't drive the bus during + * selection. + */ + SET_5380_REG(NCR5380_TCOM, 0); + SET_5380_REG(NCR5380_ICOM, 0); + + /* + * Arbitrate for the bus. + */ + SET_5380_REG(NCR5380_DATA, SC_HOST_ID); + SET_5380_REG(NCR5380_MODE, SC_ARBIT); + + splx(sps); + + cnt = 10000; + while (!(GET_5380_REG(NCR5380_ICOM) & SC_AIP) && --cnt) + delay(1); + + if (!(GET_5380_REG(NCR5380_ICOM) & SC_AIP)) { + SET_5380_REG(NCR5380_MODE, IMODE_BASE); + delay(1); + PID("scsi_select3"); + + if (GET_5380_REG(NCR5380_IDSTAT) & SC_S_BSY) { + /* + * Damn, we have a connected target that we don't know + * of. Some targets seem to respond to a selection + * AFTER the selection-timeout. Try to get the target + * into the Message-out phase so we can send an ABORT + * message. We try to avoid resetting the SCSI-bus! + */ + if (!reach_msg_out(sc, sizeof(struct scsi_generic))) { + u_long len = 1; + u_char phase = PH_MSGOUT; + u_char msg = MSG_ABORT; + + transfer_pio(&phase, &msg, &len); + } + else if (GET_5380_REG(NCR5380_IDSTAT) & SC_S_BSY) + scsi_reset(sc); + } + PID("scsi_select4"); + return (-1); + } + + /* The arbitration delay is 2.2 usecs */ + delay(3); + + /* + * Check the result of the arbitration. If we failed, return -1. + */ + if (GET_5380_REG(NCR5380_ICOM) & SC_LA) { + /* + * The spec requires that we should read the data register to + * check for higher id's and check the SC_LA again. + */ + tmp[0] = GET_5380_REG(NCR5380_DATA); + if (GET_5380_REG(NCR5380_ICOM) & SC_LA) { + SET_5380_REG(NCR5380_MODE, IMODE_BASE); + SET_5380_REG(NCR5380_ICOM, 0); + DBG_SELPRINT ("Arbitration lost,deassert SC_ARBIT\n",0); + PID("scsi_select5"); + return (-1); + } + } + SET_5380_REG(NCR5380_ICOM, SC_A_SEL | SC_A_BSY); + if (GET_5380_REG(NCR5380_ICOM) & SC_LA) { + SET_5380_REG(NCR5380_MODE, IMODE_BASE); + SET_5380_REG(NCR5380_ICOM, 0); + DBG_SELPRINT ("Arbitration lost, deassert SC_A_SEL\n", 0); + PID("scsi_select6"); + return (-1); + } + /* Bus settle delay + Bus clear delay = 1.2 usecs */ + delay(2); + DBG_SELPRINT ("Arbitration complete\n", 0); + + /* + * Now that we won the arbitration, start the selection. + */ + SET_5380_REG(NCR5380_DATA, SC_HOST_ID | (1 << reqp->targ_id)); + + /* + * Raise ATN while SEL is true before BSY goes false from arbitration, + * since this is the only way to guarantee that we'll get a MESSAGE OUT + * phase immediately after the selection. + */ + SET_5380_REG(NCR5380_ICOM, SC_A_BSY | SC_A_SEL | SC_A_ATN | SC_ADTB); + SET_5380_REG(NCR5380_MODE, IMODE_BASE); + + /* + * Turn off reselection interrupts + */ + SET_5380_REG(NCR5380_IDSTAT, 0); + + /* + * Reset BSY. The delay following it, surpresses a glitch in the + * 5380 which causes us to see our own BSY signal instead of that of + * the target. + */ + SET_5380_REG(NCR5380_ICOM, SC_A_SEL | SC_A_ATN | SC_ADTB); + delay(1); + + /* + * Wait for the target to react, the specs call for a timeout of + * 250 ms. + */ + cnt = 25000 /* 250 */; + while (!(GET_5380_REG(NCR5380_IDSTAT) & SC_S_BSY) && --cnt) + delay(10); + + if (!(GET_5380_REG(NCR5380_IDSTAT) & SC_S_BSY)) { + /* + * There is no reaction from the target, start the selection + * timeout procedure. We release the databus but keep SEL + * asserted. After that we wait a 'selection abort time' (200 + * usecs) and 2 deskew delays (90 ns) and check BSY again. + * When BSY is asserted, we assume the selection succeeded, + * otherwise we release the bus. + */ + SET_5380_REG(NCR5380_ICOM, SC_A_SEL | SC_A_ATN); + delay(201); + if (!(GET_5380_REG(NCR5380_IDSTAT) & SC_S_BSY)) { + SET_5380_REG(NCR5380_ICOM, 0); + reqp->xs->error = code ? code : XS_SELTIMEOUT; + DBG_SELPRINT ("Target %d not responding to sel\n", + reqp->targ_id); + finish_req(reqp); + PID("scsi_select7"); + return (0); + } + } + SET_5380_REG(NCR5380_ICOM, SC_A_ATN); + + DBG_SELPRINT ("Target %d responding to select.\n", reqp->targ_id); + + /* + * The SCSI-interrupts are disabled while a request is being handled. + */ + scsi_idisable(); + + /* + * Since we followed the SCSI-spec and raised ATN while SEL was true + * but before BSY was false during the selection, a 'MESSAGE OUT' + * phase should follow. Here we send an 'IDENTIFY' message. + * Allow disconnect only when interrups are allowed. + */ + tmp[0] = MSG_IDENTIFY(reqp->targ_lun, + (reqp->dr_flag & DRIVER_NOINT) ? 0 : 1); + cnt = 1; + phase = PH_MSGOUT; + if (transfer_pio(&phase, tmp, &cnt) || cnt) { + DBG_SELPRINT ("Target %d: failed to send identify\n", + reqp->targ_id); + /* + * Try to disconnect from the target. We cannot leave it just + * hanging here. + */ + if (!reach_msg_out(sc, sizeof(struct scsi_generic))) { + u_long len = 1; + u_char phase = PH_MSGOUT; + u_char msg = MSG_ABORT; + + transfer_pio(&phase, &msg, &len); + } + else scsi_reset(sc); + + SET_5380_REG(NCR5380_ICOM, 0); + reqp->xs->error = code ? code : XS_DRIVER_STUFFUP; + finish_req(reqp); + PID("scsi_select8"); + return (0); + } + reqp->phase = PH_MSGOUT; + +#ifdef notyet /* LWP: Do we need timeouts in the driver? */ + /* + * Command is connected, start timer ticking. + */ + ccb_p->xtimeout = ccb_p->timeout + Lbolt; +#endif + + connected = reqp; + busy |= 1 << reqp->targ_id; + PID("scsi_select9"); + return (0); +} + +/* + * Return codes: + * -1: quit main, trigger on interrupt + * 0: keep on running main. + */ +static int +information_transfer() +{ + SC_REQ *reqp = connected; + u_char tmp, phase; + u_long len; + + PID("info_transf1"); + /* + * Clear pending interrupts from 5380-chip. + */ + scsi_clr_ipend(); + + /* + * We only have a valid SCSI-phase when REQ is asserted. Something + * is deadly wrong when BSY has dropped. + */ + tmp = GET_5380_REG(NCR5380_IDSTAT); + + if (!(tmp & SC_S_BSY)) { + busy &= ~(1 << reqp->targ_id); + connected = NULL; + reqp->xs->error = XS_BUSY; + finish_req(reqp); + PID("info_transf2"); + return (0); + } + + if (tmp & SC_S_REQ) { + phase = (tmp >> 2) & 7; + if (phase != reqp->phase) { + reqp->phase = phase; + DBG_INFPRINT(show_phase, reqp, phase); + } + } + else return (-1); + + switch (phase) { + case PH_DATAOUT: + +#ifdef DBG_NOWRITE + ncr_tprint(reqp, "NOWRITE set -- write attempt aborted."); + reqp->msgout = MSG_ABORT; + SET_5380_REG(NCR5380_ICOM, SC_A_ATN); + return (-1); +#endif /* DBG_NOWRITE */ + + case PH_DATAIN: +#ifdef REAL_DMA + if (reqp->dr_flag & DRIVER_DMAOK) { + int poll = REAL_DMA_POLL|(reqp->dr_flag & DRIVER_NOINT); + transfer_dma(reqp, phase, poll); + if (!poll) + return (0); + } + else +#endif + { + PID("info_transf3"); + len = reqp->xdata_len; +#ifdef USE_PDMA + transfer_pdma(&phase, reqp->xdata_ptr, &len); +#else + transfer_pio(&phase, reqp->xdata_ptr, &len); +#endif + reqp->xdata_ptr += reqp->xdata_len - len; + reqp->xdata_len = len; + } + return (-1); + case PH_MSGIN: + /* + * We only expect single byte messages here. + */ + len = 1; + transfer_pio(&phase, &tmp, &len); + reqp->message = tmp; + return (handle_message(reqp, tmp)); + case PH_MSGOUT: + len = 1; + transfer_pio(&phase, &reqp->msgout, &len); + if (reqp->msgout == MSG_ABORT) { + busy &= ~(1 << reqp->targ_id); + connected = NULL; + reqp->xs->error = XS_DRIVER_STUFFUP; + finish_req(reqp); + PID("info_transf4"); + return (0); + } + reqp->msgout = MSG_NOOP; + return (-1); + case PH_CMD : + len = command_size(reqp->xcmd.opcode); + transfer_pio(&phase, (u_char *)&reqp->xcmd, &len); + PID("info_transf5"); + return (-1); + case PH_STATUS: + len = 1; + transfer_pio(&phase, &tmp, &len); + reqp->status = tmp; + PID("info_transf6"); + return (-1); + default : + ncr_tprint(reqp, "Unknown phase\n"); + } + PID("info_transf7"); + return (-1); +} + +/* + * Handle the message 'msg' send to us by the target. + * Return values: + * 0 : The current command has completed. + * -1 : Get on to the next phase. + */ +static int +handle_message(reqp, msg) +SC_REQ *reqp; +u_int msg; +{ + int sps; + + PID("hmessage1"); + switch (msg) { + /* + * Linking lets us reduce the time required to get + * the next command to the device, skipping the arbitration + * and selection time. In the current implementation, + * we merely have to start the next command pointed + * to by 'next_link'. + */ + case MSG_LINK_CMD_COMPLETE: + case MSG_LINK_CMD_COMPLETEF: + if (reqp->link == NULL) { + ncr_tprint(reqp, "No link for linked command"); + reqp->msgout = MSG_ABORT; + SET_5380_REG(NCR5380_ICOM, SC_A_ATN); + PID("hmessage2"); + return (-1); + } + reqp->xs->error = 0; + +#ifdef AUTO_SENSE + if (check_autosense(reqp, 0) == -1) + return (-1); +#endif /* AUTO_SENSE */ + +#ifdef DBG_REQ + show_request(reqp->link, "LINK"); +#endif + connected = reqp->link; + finish_req(reqp); + PID("hmessage3"); + return (-1); + case MSG_ABORT: + case MSG_CMDCOMPLETE: +#ifdef DBG_REQ + show_request(reqp, "DONE"); +#endif + connected = NULL; + busy &= ~(1 << reqp->targ_id); + if (!(reqp->dr_flag & DRIVER_AUTOSEN)) + reqp->xs->resid = reqp->xdata_len; + reqp->xs->error = 0; + +#ifdef AUTO_SENSE + if (check_autosense(reqp, 0) == -1) { + PID("hmessage4"); + return (0); + } +#endif /* AUTO_SENSE */ + + finish_req(reqp); + PID("hmessage5"); + return (0); + case MSG_MESSAGE_REJECT: + PID("hmessage6"); + return (-1); + case MSG_DISCONNECT: +#ifdef DBG_REQ + show_request(reqp, "DISCON"); +#endif + sps = splbio(); + connected = NULL; + reqp->next = discon_q; + discon_q = reqp; + splx(sps); + PID("hmessage7"); + return (0); + case MSG_SAVEDATAPOINTER: + case MSG_RESTOREPOINTERS: + /* + * We save pointers implicitely at disconnect. + * So we can ignore these messages. + */ + PID("hmessage8"); + return (-1); + default: + ncr_tprint(reqp, "Unkown message %x\n", msg); + return (-1); + } + PID("hmessage9"); + return (-1); +} + +/* + * Handle reselection. If a valid reconnection occurs, connected + * points at the reconnected command. The command is removed from the + * disconnected queue. + */ +static void +reselect(sc) +struct ncr_softc *sc; +{ + u_char phase; + u_long len; + u_char msg; + u_char target_mask; + int abort = 0; + SC_REQ *tmp, *prev; + + PID("reselect1"); + target_mask = GET_5380_REG(NCR5380_DATA) & ~SC_HOST_ID; + + /* + * At this point, we have detected that our SCSI-id is on the bus, + * SEL is true and BSY was false for at least one bus settle + * delay (400 ns.). + * We must assert BSY ourselves, until the target drops the SEL signal. + * This should happen within 2 deskew delays (2 * 45ns.) + */ + SET_5380_REG(NCR5380_ICOM, SC_A_BSY); + while (GET_5380_REG(NCR5380_IDSTAT) & SC_S_SEL) + ; + + SET_5380_REG(NCR5380_ICOM, 0); + + /* + * Get the expected identify message. + */ + phase = PH_MSGIN; + len = 1; + transfer_pio(&phase, &msg, &len); + if (len || !MSG_ISIDENTIFY(msg)) { + ncr_aprint(sc, "Expecting IDENTIFY, got 0x%x\n", msg); + abort = 1; + } + else { + /* + * Find the command reconnecting + */ + for (tmp = discon_q, prev = NULL; tmp; prev = tmp, tmp = tmp->next){ + if (target_mask == (1 << tmp->targ_id)) { + if (prev) + prev->next = tmp->next; + else discon_q = tmp->next; + tmp->next = NULL; + break; + } + } + if (tmp == NULL) { + ncr_aprint(sc, "No disconnected job for targetmask %x\n", + target_mask); + abort = 1; + } + } + if (abort) { + msg = MSG_ABORT; + len = 1; + phase = PH_MSGOUT; + + SET_5380_REG(NCR5380_ICOM, SC_A_ATN); + transfer_pio(&phase, &msg, &len); + } + else { + connected = tmp; +#ifdef DBG_REQ + show_request(tmp, "RECON"); +#endif + } + PID("reselect2"); +} + +/* + * Transfer data in a given phase using programmed I/O. + * Returns -1 when a different phase is entered without transferring the + * maximum number of bytes, 0 if all bytes or exit is in the same + * phase. + */ +static int +transfer_pio(phase, data, len) +u_char *phase; +u_char *data; +u_long *len; +{ + u_int cnt = *len; + u_char ph = *phase; + u_char tmp; + + DBG_PIOPRINT ("SCSI: transfer_pio start: phase: %d, len: %d\n", ph,cnt); + PID("tpio1"); + SET_5380_REG(NCR5380_TCOM, ph); + do { + if (!wait_req_true()) { + DBG_PIOPRINT ("SCSI: transfer_pio: missing REQ\n", 0, 0); + break; + } + if (((GET_5380_REG(NCR5380_IDSTAT) >> 2) & 7) != ph) { + DBG_PIOPRINT ("SCSI: transfer_pio: phase mismatch\n", 0, 0); + break; + } + if (PH_IN(ph)) { + *data++ = GET_5380_REG(NCR5380_DATA); + SET_5380_REG(NCR5380_ICOM, SC_A_ACK); + } + else { + SET_5380_REG(NCR5380_DATA, *data++); + + /* + * The SCSI-standard suggests that in the 'MESSAGE OUT' phase, + * the initiator should drop ATN on the last byte of the + * message phase after REQ has been asserted for the handshake + * but before the initiator raises ACK. + */ + if (!( (ph == PH_MSGOUT) && (cnt > 1) )) { + SET_5380_REG(NCR5380_ICOM, SC_ADTB); + SET_5380_REG(NCR5380_ICOM, SC_ADTB | SC_A_ACK); + } + else { + SET_5380_REG(NCR5380_ICOM, SC_ADTB | SC_A_ATN); + SET_5380_REG(NCR5380_ICOM, SC_ADTB|SC_A_ATN|SC_A_ACK); + } + } + if (!wait_req_false()) { + DBG_PIOPRINT ("SCSI: transfer_pio - REQ not dropping\n", 0, 0); + break; + } + + if (!( (ph == PH_MSGOUT) && (cnt > 1) )) + SET_5380_REG(NCR5380_ICOM, 0); + else SET_5380_REG(NCR5380_ICOM, SC_A_ATN); + } while (--cnt); + + if ((tmp = GET_5380_REG(NCR5380_IDSTAT)) & SC_S_REQ) + *phase = (tmp >> 2) & 7; + else *phase = NR_PHASE; + *len = cnt; + DBG_PIOPRINT ("SCSI: transfer_pio done: phase: %d, len: %d\n", + *phase, cnt); + PID("tpio2"); + if (!cnt || (*phase == ph)) + return (0); + return (-1); +} + +#ifdef REAL_DMA +/* + * Start a DMA-transfer on the device using the current pointers. + * If 'poll' is true, the function busy-waits until DMA has completed. + */ +static void +transfer_dma(reqp, phase, poll) +SC_REQ *reqp; +u_int phase; +int poll; +{ + int dma_done; + u_char mbase = 0; + int sps; + +again: + PID("tdma1"); + /* + * We should be in phase, otherwise we are not allowed to + * drive the bus. + */ + SET_5380_REG(NCR5380_TCOM, phase); + + /* + * Defer interrupts until DMA is fully running. + */ + sps = splbio(); + + /* + * Clear pending interrupts and parity errors. + */ + scsi_clr_ipend(); + + if (!poll) { + /* + * Enable SCSI interrupts and set IN_DMA flag, set 'mbase' + * to the interrupts we want enabled. + */ + scsi_ienable(); + reqp->dr_flag |= DRIVER_IN_DMA; + mbase = SC_E_EOPI | SC_MON_BSY; + } + else scsi_idisable(); + mbase |= IMODE_BASE | SC_M_DMA; + scsi_dma_setup(reqp, phase, mbase); + + splx(sps); + + if (poll) { + /* + * On polled-dma transfers, we wait here until the + * 'end-of-dma' condition occurs. + */ + poll_edma(reqp); + if (!(dma_done = dma_ready())) + goto again; + } + PID("tdma2"); +} + +/* + * Check results of a DMA data-transfer. + */ +static int +dma_ready() +{ + SC_REQ *reqp = connected; + int dmstat, is_edma; + long bytes_left, bytes_done; + + is_edma = get_dma_result(reqp, &bytes_left); + dmstat = GET_5380_REG(NCR5380_DMSTAT); + + /* + * Check if the call is sensible and not caused by any spurious + * interrupt. + */ + if (!is_edma && !(dmstat & (SC_END_DMA|SC_BSY_ERR)) + && (dmstat & SC_PHS_MTCH) ) { + ncr_tprint(reqp, "dma_ready: spurious call" + "(dm:%x,last_hit: %s)\n", +#ifdef DBG_PID + dmstat, last_hit); +#else + dmstat, "unknown"); +#endif + return (0); + } + + /* + * Clear all (pending) interrupts. + */ + scsi_clr_ipend(); + + /* + * Update various transfer-pointers/lengths + */ + bytes_done = reqp->dm_cur->dm_count - bytes_left; + + reqp->xdata_ptr = &reqp->xdata_ptr[bytes_done]; /* XXX */ + reqp->xdata_len -= bytes_done; /* XXX */ + if ((reqp->dm_cur->dm_count -= bytes_done) == 0) + reqp->dm_cur++; + else reqp->dm_cur->dm_addr += bytes_done; + + if (PH_IN(reqp->phase) && (dmstat & SC_PAR_ERR)) { + if (!(ncr5380_no_parchk & (1 << reqp->targ_id))) + /* XXX: Should be parity error ???? */ + reqp->xs->error = XS_DRIVER_STUFFUP; + } + + /* + * DMA mode should always be reset even when we will continue with the + * next chain. + */ + SET_5380_REG(NCR5380_MODE, GET_5380_REG(NCR5380_MODE) & ~SC_M_DMA); + + + if ((dmstat & SC_BSY_ERR) || !(dmstat & SC_PHS_MTCH) + || (reqp->dm_cur > reqp->dm_last) || (reqp->xs->error)) { + /* + * Tell interrupt functions DMA mode has ended. + */ + reqp->dr_flag &= ~DRIVER_IN_DMA; + + /* + * Clear mode and icom + */ + SET_5380_REG(NCR5380_MODE, IMODE_BASE); + SET_5380_REG(NCR5380_ICOM, 0); + + if (dmstat & SC_BSY_ERR) { + if (!reqp->xs->error) + reqp->xs->error = XS_BUSY; + finish_req(reqp); + PID("dma_ready1"); + return (1); + } + + if (reqp->xs->error != 0) { +ncr_tprint(reqp, "dma-ready: code = %d\n", reqp->xs->error); /* LWP */ + reqp->msgout = MSG_ABORT; + SET_5380_REG(NCR5380_ICOM, SC_A_ATN); + } + PID("dma_ready2"); + return (1); + } + return (0); +} +#endif /* REAL_DMA */ + +static int +check_autosense(reqp, linked) +SC_REQ *reqp; +int linked; +{ + int sps; + + /* + * If we not executing an auto-sense and the status code + * is request-sense, we automatically issue a request + * sense command. + */ + PID("cautos1"); + if (!(reqp->dr_flag & DRIVER_AUTOSEN)) { + if (reqp->status == SCSCHKC) { + memcpy(&reqp->xcmd, sense_cmd, sizeof(sense_cmd)); + reqp->xdata_ptr = (u_char *)&reqp->xs->sense; + reqp->xdata_len = sizeof(reqp->xs->sense); + reqp->dr_flag |= DRIVER_AUTOSEN; + reqp->dr_flag &= ~DRIVER_DMAOK; + if (!linked) { + sps = splbio(); + reqp->next = issue_q; + issue_q = reqp; + splx(sps); + } + else reqp->xcmd.bytes[4] |= 1; + +#ifdef DBG_REQ + show_request(reqp, "AUTO-SENSE"); +#endif + PID("cautos2"); + return (-1); + } + } + else { + /* + * An auto-sense has finished + */ + if ((reqp->status & SCSMASK) != SCSGOOD) + reqp->xs->error = XS_DRIVER_STUFFUP; /* SC_E_AUTOSEN; */ + else reqp->xs->error = XS_SENSE; + reqp->status = SCSCHKC; + } + PID("cautos3"); + return (0); +} + +static int +reach_msg_out(sc, len) +struct ncr_softc *sc; +u_long len; +{ + u_char phase; + u_char data; + + ncr_aprint(sc, "Trying to reach Message-out phase\n"); + if ((phase = GET_5380_REG(NCR5380_IDSTAT)) & SC_S_REQ) + phase = (phase >> 2) & 7; + else return (-1); + ncr_aprint(sc, "Trying to reach Message-out phase, now: %d\n", phase); + if (phase == PH_MSGOUT) + return (0); + + SET_5380_REG(NCR5380_TCOM, phase); + + do { + if (!wait_req_true()) + break; + if (((GET_5380_REG(NCR5380_IDSTAT) >> 2) & 7) != phase) + break; + if (PH_IN(phase)) { + data = GET_5380_REG(NCR5380_DATA); + SET_5380_REG(NCR5380_ICOM, SC_A_ACK | SC_A_ATN); + } + else { + SET_5380_REG(NCR5380_DATA, 0); + SET_5380_REG(NCR5380_ICOM, SC_ADTB|SC_A_ACK|SC_A_ATN); + } + if (!wait_req_false()) + break; + SET_5380_REG(NCR5380_ICOM, SC_A_ATN); + } while (--len); + + if ((phase = GET_5380_REG(NCR5380_IDSTAT)) & SC_S_REQ) { + phase = (phase >> 2) & 7; + if (phase == PH_MSGOUT) { + ncr_aprint(sc, "Message-out phase reached.\n"); + return (0); + } + } + return (-1); +} + +static void +scsi_reset(sc) +struct ncr_softc *sc; +{ + SC_REQ *tmp, *next; + int sps; + + ncr_aprint(sc, "Resetting SCSI-bus\n"); + + PID("scsi_reset1"); + sps = splbio(); + SET_5380_REG(NCR5380_ICOM, SC_A_RST); + delay(1); + SET_5380_REG(NCR5380_ICOM, 0); + + /* + * None of the jobs in the discon_q will ever be reconnected, + * notify this to the higher level code. + */ + for (tmp = discon_q; tmp ;) { + next = tmp->next; + tmp->next = NULL; + tmp->xs->error = XS_TIMEOUT; + busy &= ~(1 << tmp->targ_id); + finish_req(tmp); + tmp = next; + } + discon_q = NULL; + + /* + * The current job will never finish either. + * The problem is that we can't finish the job because an instance + * of main is running on it. Our best guess is that the job is currently + * doing REAL-DMA. In that case 'dma_ready()' should correctly finish + * the job because it detects BSY-loss. + */ + if (tmp = connected) { + if (tmp->dr_flag & DRIVER_IN_DMA) { + tmp->xs->error = XS_DRIVER_STUFFUP; +#ifdef REAL_DMA + dma_ready(); +#endif + } + } + splx(sps); + PID("scsi_reset2"); +} + +/* + * Check validity of the IRQ set by the 5380. If the interrupt is valid, + * the appropriate action is carried out (reselection or DMA ready) and + * INTR_RESEL or INTR_DMA is returned. Otherwise a console notice is written + * and INTR_SPURIOUS is returned. + */ +static int +check_intr(sc) +struct ncr_softc *sc; +{ + SC_REQ *reqp; + + if ((GET_5380_REG(NCR5380_IDSTAT) & (SC_S_SEL|SC_S_IO)) + ==(SC_S_SEL|SC_S_IO)) + return (INTR_RESEL); + else { + if ((reqp = connected) && (reqp->dr_flag & DRIVER_IN_DMA)){ + reqp->dr_flag &= ~DRIVER_IN_DMA; + return (INTR_DMA); + } + } + scsi_clr_ipend(); + printf("-->"); + scsi_show(); + ncr_aprint(sc, "Spurious interrupt.\n"); + return (INTR_SPURIOUS); +} + +#ifdef REAL_DMA +/* + * Check if DMA can be used for this request. This function also builds + * the dma-chain. + */ +static int +scsi_dmaok(reqp) +SC_REQ *reqp; +{ + u_long phy_buf; + u_long phy_len; + void *req_addr; + u_long req_len; + struct dma_chain *dm; + + /* + * Initialize locals and requests' DMA-chain. + */ + req_len = reqp->xdata_len; + req_addr = (void*)reqp->xdata_ptr; + dm = reqp->dm_cur = reqp->dm_last = reqp->dm_chain; + + dm->dm_count = dm->dm_addr = 0; + + /* + * Do not accept zero length DMA. + */ + if (req_len == 0) + return (0); + + /* + * LWP: I think that this restriction is not strictly nessecary. + */ + if ((req_len & 0x1) || ((u_int)req_addr & 0x3)) + return (0); + + /* + * Build the DMA-chain. + */ + dm->dm_addr = phy_buf = kvtop(req_addr); + while (req_len) { + if (req_len < (phy_len = NBPG - ((u_long)req_addr & PGOFSET))) + phy_len = req_len; + + req_addr += phy_len; + req_len -= phy_len; + dm->dm_count += phy_len; + + if (req_len) { + u_long tmp = kvtop(req_addr); + + if ((phy_buf + phy_len) != tmp) { + if (wrong_dma_range(reqp, dm)) + return (0); + + if (++dm >= &reqp->dm_chain[MAXDMAIO]) { + ncr_tprint(reqp,"dmaok: DMA chain too long!\n"); + return (0); + } + dm->dm_count = 0; + dm->dm_addr = tmp; + } + phy_buf = tmp; + } + } + if (wrong_dma_range(reqp, dm)) + return (0); + reqp->dm_last = dm; + return (1); +} +#endif /* REAL_DMA */ + +static void +run_main(sc) +struct ncr_softc *sc; +{ + int sps = splbio(); + + callback_scheduled = 0; + if (!main_running) { + /* + * If shared resources are required, claim them + * before entering 'scsi_main'. If we can't get them + * now, assume 'run_main' will be called when the resource + * becomes available. + */ + if (!claimed_dma()) { + splx(sps); + return; + } + main_running = 1; + splx(sps); + scsi_main(sc); + } + else splx(sps); +} + +/* + * Prefix message with full target info. + */ +static void +ncr_tprint(SC_REQ *reqp, char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + sc_print_addr(reqp->xs->sc_link); + printf(fmt, ap); + va_end(ap); +} + +/* + * Prefix message with adapter info. + */ +static void +ncr_aprint(struct ncr_softc *sc, char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + printf("%s :", sc->sc_dev.dv_xname); + printf(fmt, ap); + va_end(ap); +} +/**************************************************************************** + * Start Debugging Functions * + ****************************************************************************/ +static void +show_data_sense(xs) +struct scsi_xfer *xs; +{ + u_char *b; + int i; + int sz; + + b = (u_char *) xs->cmd; + printf("cmd[%d,%d]: ", xs->cmdlen, sz = command_size(*b)); + for (i = 0; i < sz; i++) + printf("%x ", b[i]); + printf("\nsense: "); + b = (u_char *)&xs->sense; + for (i = 0; i < sizeof(xs->sense); i++) + printf("%x ", b[i]); + printf("\n"); +} + +static void +show_request(reqp, qtxt) +SC_REQ *reqp; +char *qtxt; +{ + printf("REQ-%s: %d %x[%d] cmd[0]=%x S=%x M=%x R=%x %s\n", qtxt, + reqp->targ_id, reqp->xdata_ptr, reqp->xdata_len, + reqp->xcmd.opcode, reqp->status, reqp->message, + reqp->xs->error, reqp->link ? "L" : ""); + if (reqp->status == SCSCHKC) + show_data_sense(reqp->xs); +} + +scsi_show() +{ + SC_REQ *tmp; + int sps = splhigh(); + +#ifndef DBG_PID + #define last_hit "" +#endif + + for (tmp = issue_q; tmp; tmp = tmp->next) + show_request(tmp, "ISSUED"); + for (tmp = discon_q; tmp; tmp = tmp->next) + show_request(tmp, "DISCONNECTED"); + if (connected) + show_request(connected, "CONNECTED"); + /* show_signals(); */ + if (connected) + printf("phase = %d, ", connected->phase); + printf("busy:%x, last_hit:%s, spl:%04x\n", busy, last_hit, sps); + + splx(sps); +} diff --git a/sys/arch/pc532/dev/ncr5380reg.h b/sys/arch/pc532/dev/ncr5380reg.h new file mode 100644 index 000000000000..f90f30f1c010 --- /dev/null +++ b/sys/arch/pc532/dev/ncr5380reg.h @@ -0,0 +1,241 @@ +/* $NetBSD: ncr5380reg.h,v 1.1 1995/08/25 07:30:36 phil Exp $ */ + +/* + * Copyright (c) 1995 Leo Weppelman. + * 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 Leo Weppelman. + * 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. + */ + +#ifndef _NCR5380REG_H +#define _NCR5380REG_H +/* + * NCR5380 common interface definitions. + */ + +/* + * Register numbers: (first argument to GET/SET_5380_REG ) + */ +#define NCR5380_DATA 0 /* Data register */ +#define NCR5380_ICOM 1 /* Initiator command register */ +#define NCR5380_MODE 2 /* Mode register */ +#define NCR5380_TCOM 3 /* Target command register */ +#define NCR5380_IDSTAT 4 /* Bus status register */ +#define NCR5380_DMSTAT 5 /* DMA status register */ +#define NCR5380_TRCV 6 /* Target receive register */ +#define NCR5380_IRCV 7 /* Initiator receive register */ + +/* + * Definitions for Initiator command register. + */ +#define SC_A_RST 0x80 /* RW - Assert RST */ +#define SC_TEST 0x40 /* W - Test mode */ +#define SC_AIP 0x40 /* R - Arbitration in progress */ +#define SC_LA 0x20 /* R - Lost arbitration */ +#define SC_A_ACK 0x10 /* RW - Assert ACK */ +#define SC_A_BSY 0x08 /* RW - Assert BSY */ +#define SC_A_SEL 0x04 /* RW - Assert SEL */ +#define SC_A_ATN 0x02 /* RW - Assert ATN */ +#define SC_ADTB 0x01 /* RW - Assert Data To Bus */ + +/* + * Definitions for mode register + */ +#define SC_B_DMA 0x80 /* RW - Block mode DMA (not on TT!) */ +#define SC_T_MODE 0x40 /* RW - Target mode */ +#define SC_E_PAR 0x20 /* RW - Enable parity check */ +#define SC_E_PARI 0x10 /* RW - Enable parity interrupt */ +#define SC_E_EOPI 0x08 /* RW - Enable End Of Process Interrupt */ +#define SC_MON_BSY 0x04 /* RW - Monitor BSY */ +#define SC_M_DMA 0x02 /* RW - Set DMA mode */ +#define SC_ARBIT 0x01 /* RW - Arbitrate */ + +/* + * Definitions for tcom register + */ +#define SC_LBS 0x80 /* RW - Last Byte Send (not on TT!) */ +#define SC_A_REQ 0x08 /* RW - Assert REQ */ +#define SC_A_MSG 0x04 /* RW - Assert MSG */ +#define SC_A_CD 0x02 /* RW - Assert C/D */ +#define SC_A_IO 0x01 /* RW - Assert I/O */ + +/* + * Definitions for idstat register + */ +#define SC_S_RST 0x80 /* R - RST is set */ +#define SC_S_BSY 0x40 /* R - BSY is set */ +#define SC_S_REQ 0x20 /* R - REQ is set */ +#define SC_S_MSG 0x10 /* R - MSG is set */ +#define SC_S_CD 0x08 /* R - C/D is set */ +#define SC_S_IO 0x04 /* R - I/O is set */ +#define SC_S_SEL 0x02 /* R - SEL is set */ +#define SC_S_PAR 0x01 /* R - Parity bit */ + +/* + * Definitions for dmastat register + */ +#define SC_END_DMA 0x80 /* R - End of DMA */ +#define SC_DMA_REQ 0x40 /* R - DMA request */ +#define SC_PAR_ERR 0x20 /* R - Parity error */ +#define SC_IRQ_SET 0x10 /* R - IRQ is active */ +#define SC_PHS_MTCH 0x08 /* R - Phase Match */ +#define SC_BSY_ERR 0x04 /* R - Busy error */ +#define SC_ATN_STAT 0x02 /* R - State of ATN line */ +#define SC_ACK_STAT 0x01 /* R - State of ACK line */ +#define SC_S_SEND 0x00 /* W - Start DMA output */ + +#define SC_CLINT { /* Clear interrupts */ \ + int i = GET_5380_REG(NCR5380_IRCV); \ + } + + +/* + * Definition of SCSI-bus phases. The values are determined by signals + * on the SCSI-bus. DO NOT CHANGE! + * The values must be used to index the pointers in SCSI-PARMS. + */ +#define NR_PHASE 8 +#define PH_DATAOUT 0 +#define PH_DATAIN 1 +#define PH_CMD 2 +#define PH_STATUS 3 +#define PH_RES1 4 +#define PH_RES2 5 +#define PH_MSGOUT 6 +#define PH_MSGIN 7 + +#define PH_OUT(phase) (!(phase & 1)) /* TRUE if output phase */ +#define PH_IN(phase) (phase & 1) /* TRUE if input phase */ + +/* + * Id of Host-adapter + */ +#define SC_HOST_ID 0x80 + +/* + * Base setting for 5380 mode register + */ +#define IMODE_BASE SC_E_PAR + +/* + * SCSI completion status codes, should move to sys/scsi/???? + */ +#define SCSMASK 0x1e /* status code mask */ +#define SCSGOOD 0x00 /* good status */ +#define SCSCHKC 0x02 /* check condition */ +#define SCSBUSY 0x08 /* busy status */ +#define SCSCMET 0x04 /* condition met / good */ + +/* + * Return values of check_intr() + */ +#define INTR_SPURIOUS 0 +#define INTR_RESEL 2 +#define INTR_DMA 3 + +struct ncr_softc { + struct device sc_dev; + struct scsi_link sc_link; +}; + +/* + * Max. number of dma-chains per request + */ +#define MAXDMAIO (MAXPHYS/NBPG + 1) + +/* + * Some requests are not contiguous in physical memory. We need to break them + * up into contiguous parts for DMA. + */ +struct dma_chain { + u_int dm_count; + u_long dm_addr; +}; + +/* + * Define our issue, free and disconnect queue's. + */ +typedef struct req_q { + struct req_q *next; /* next in free, issue or discon queue */ + struct req_q *link; /* next linked command to execute */ + struct scsi_xfer *xs; /* request from high-level driver */ + u_char dr_flag; /* driver state */ + u_char phase; /* current SCSI phase */ + u_char msgout; /* message to send when requested */ + u_char targ_id; /* target for command */ + u_char targ_lun; /* lun for command */ + u_char status; /* returned status byte */ + u_char message; /* returned message byte */ + struct dma_chain dm_chain[MAXDMAIO]; + struct dma_chain *dm_cur; /* current dma-request */ + struct dma_chain *dm_last; /* last dma-request */ + long xdata_len; /* length of transfer */ + u_char *xdata_ptr; /* physical address of transfer */ + struct scsi_generic xcmd; /* command to execute */ +} SC_REQ; + +/* + * Values for dr_flag: + */ +#define DRIVER_IN_DMA 1 /* Non-polled DMA activated */ +#define DRIVER_AUTOSEN 2 /* Doing automatic sense */ +#define DRIVER_NOINT 4 /* We are booting: no interrupts */ +#define DRIVER_DMAOK 8 /* DMA can be used on this request */ + +/* XXX: Should go to ncr5380var.h */ +static SC_REQ *issue_q = NULL; /* Commands waiting to be issued*/ +static SC_REQ *discon_q = NULL; /* Commands disconnected */ +static SC_REQ *connected = NULL; /* Command currently connected */ + +/* + * Function decls: + */ +static int transfer_pio __P((u_char *, u_char *, u_long *)); +static int wait_req_true __P((void)); +static int wait_req_false __P((void)); +static int scsi_select __P((SC_REQ *, int)); +static int handle_message __P((SC_REQ *, u_int)); +static int information_transfer __P((void)); +static void reselect __P((struct ncr_softc *)); +static int dma_ready __P((void)); +static void transfer_dma __P((SC_REQ *, u_int, int)); +static int check_autosense __P((SC_REQ *, int)); +static int reach_msg_out __P((struct ncr_softc *, u_long)); +static int check_intr __P((struct ncr_softc *)); +static void scsi_reset __P((struct ncr_softc *)); +static int scsi_dmaok __P((SC_REQ *)); +static void run_main __P((struct ncr_softc *)); +static void scsi_main __P((struct ncr_softc *)); +static void ncr_ctrl_intr __P((struct ncr_softc *)); +static void ncr_dma_intr __P((struct ncr_softc *)); +static void ncr_tprint __P((SC_REQ *, char *, ...)); +static void ncr_aprint __P((struct ncr_softc *, char *, ...)); + +static void show_request __P((SC_REQ *, char *)); +static void show_phase __P((SC_REQ *, int)); +static void show_signals __P((void)); + +#endif /* _NCR5380REG_H */ diff --git a/sys/arch/pc532/dev/ncr_5380.h b/sys/arch/pc532/dev/ncr_5380.h new file mode 100644 index 000000000000..4d209f5671fe --- /dev/null +++ b/sys/arch/pc532/dev/ncr_5380.h @@ -0,0 +1,145 @@ +/* $NetBSD: ncr_5380.h,v 1.4 1995/08/25 07:30:37 phil Exp $ */ + +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ +/* + * File: scsi_5380.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 5/91 + * + * Defines for the NCR 5380 (SCSI chip), aka Am5380 + * + * Modified for the pc532 by Phil Nelson. 1/94 + */ + +/* + * Register map + */ + +typedef struct { + volatile unsigned char sci_data; /* r: Current data */ +#define sci_odata sci_data /* w: Out data */ + + volatile unsigned char sci_icmd; /* rw: Initiator command */ + + volatile unsigned char sci_mode; /* rw: Mode */ + + volatile unsigned char sci_tcmd; /* rw: Target command */ + + volatile unsigned char sci_bus_csr; /* r: Bus Status */ +#define sci_sel_enb sci_bus_csr /* w: Select enable */ + + volatile unsigned char sci_csr; /* r: Status */ +#define sci_dma_send sci_csr /* w: Start dma send data */ + + volatile unsigned char sci_idata; /* r: Input data */ +#define sci_trecv sci_idata /* w: Start dma receive, target */ + + volatile unsigned char sci_iack; /* r: Interrupt Acknowledge */ +#define sci_irecv sci_iack /* w: Start dma receive, initiator */ +} sci_regmap_t; + + +/* + * Initiator command register + */ + +#define SCI_ICMD_DATA 0x01 /* rw: Assert data bus */ +#define SCI_ICMD_ATN 0x02 /* rw: Assert ATN signal */ +#define SCI_ICMD_SEL 0x04 /* rw: Assert SEL signal */ +#define SCI_ICMD_BSY 0x08 /* rw: Assert BSY signal */ +#define SCI_ICMD_ACK 0x10 /* rw: Assert ACK signal */ +#define SCI_ICMD_LST 0x20 /* r: Lost arbitration */ +#define SCI_ICMD_DIFF SCI_ICMD_LST /* w: Differential cable */ +#define SCI_ICMD_AIP 0x40 /* r: Arbitration in progress */ +#define SCI_ICMD_TEST SCI_ICMD_AIP /* w: Test mode */ +#define SCI_ICMD_RST 0x80 /* rw: Assert RST signal */ + + +/* + * Mode register + */ + +#define SCI_MODE_ARB 0x01 /* rw: Start arbitration */ +#define SCI_MODE_DMA 0x02 /* rw: Enable DMA xfers */ +#define SCI_MODE_MONBSY 0x04 /* rw: Monitor BSY signal */ +#define SCI_MODE_DMA_IE 0x08 /* rw: Enable DMA complete interrupt */ +#define SCI_MODE_PERR_IE 0x10 /* rw: Interrupt on parity errors */ +#define SCI_MODE_PAR_CHK 0x20 /* rw: Check parity */ +#define SCI_MODE_TARGET 0x40 /* rw: Target mode (Initiator if 0) */ +#define SCI_MODE_BLOCKDMA 0x80 /* rw: Block-mode DMA handshake (MBZ) */ + + +/* + * Target command register + */ + +#define SCI_TCMD_IO 0x01 /* rw: Assert I/O signal */ +#define SCI_TCMD_CD 0x02 /* rw: Assert C/D signal */ +#define SCI_TCMD_MSG 0x04 /* rw: Assert MSG signal */ +#define SCI_TCMD_PHASE_MASK 0x07 /* r: Mask for current bus phase */ +#define SCI_TCMD_REQ 0x08 /* rw: Assert REQ signal */ +#define SCI_TCMD_LAST_SENT 0x80 /* ro: Last byte was xferred + * (not on 5380/1) */ + +#define SCI_PHASE(x) SCSI_PHASE(x) + +/* + * Current (SCSI) Bus status + */ + +#define SCI_BUS_DBP 0x01 /* r: Data Bus parity */ +#define SCI_BUS_SEL 0x02 /* r: SEL signal */ +#define SCI_BUS_IO 0x04 /* r: I/O signal */ +#define SCI_BUS_CD 0x08 /* r: C/D signal */ +#define SCI_BUS_MSG 0x10 /* r: MSG signal */ +#define SCI_BUS_REQ 0x20 /* r: REQ signal */ +#define SCI_BUS_BSY 0x40 /* r: BSY signal */ +#define SCI_BUS_RST 0x80 /* r: RST signal */ + +#define SCI_CUR_PHASE(x) SCSI_PHASE((x)>>2) + +/* + * Bus and Status register + */ + +#define SCI_CSR_ACK 0x01 /* r: ACK signal */ +#define SCI_CSR_ATN 0x02 /* r: ATN signal */ +#define SCI_CSR_DISC 0x04 /* r: Disconnected (BSY==0) */ +#define SCI_CSR_PHASE_MATCH 0x08 /* r: Bus and SCI_TCMD match */ +#define SCI_CSR_INT 0x10 /* r: Interrupt request */ +#define SCI_CSR_PERR 0x20 /* r: Parity error */ +#define SCI_CSR_DREQ 0x40 /* r: DMA request */ +#define SCI_CSR_DONE 0x80 /* r: DMA count is zero */ + + +/* icu scsi chip switching */ + +#define ICU_ADR 0xfffffe00 +#define ICU_IO (ICU_ADR+20) +#define ICU_DIR (ICU_ADR+21) +#define ICU_DATA (ICU_ADR+19) +#define ICU_SCSI_BIT 0x80 diff --git a/sys/arch/pc532/dev/ncr_defs.h b/sys/arch/pc532/dev/ncr_defs.h new file mode 100644 index 000000000000..952661c3b54c --- /dev/null +++ b/sys/arch/pc532/dev/ncr_defs.h @@ -0,0 +1,57 @@ +/* $NetBSD: ncr_defs.h,v 1.4 1995/08/25 07:30:39 phil Exp $ */ + +/*- + * Copyright (C) 1993 Allen K. Briggs, Chris P. Caputo, + * Michael L. Finch, Bradley A. Grantham, and + * Lawrence A. Kesteloot + * 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 the Alice Group. + * 4. The names of the Alice Group or any of its members may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``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 ALICE GROUP 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. + */ + +#ifndef _SCSI_DEFS_H +#define _SCSI_DEFS_H + +#define SCSI_PHASE_DATA_OUT 0x0 +#define SCSI_PHASE_DATA_IN 0x1 +#define SCSI_PHASE_CMD 0x2 +#define SCSI_PHASE_STATUS 0x3 +#define SCSI_PHASE_UNSPEC1 0x4 +#define SCSI_PHASE_UNSPEC2 0x5 +#define SCSI_PHASE_MESSAGE_OUT 0x6 +#define SCSI_PHASE_MESSAGE_IN 0x7 + +#define SCSI_PHASE(x) ((x)&0x7) + +/* These should be fixed up. */ + +#define SCSI_RET_SUCCESS 0 +#define SCSI_RET_RETRY 1 +#define SCSI_RET_DEVICE_DOWN 2 +#define SCSI_RET_COMMAND_FAIL 3 + +#endif diff --git a/sys/arch/pc532/dev/ncrreg.h b/sys/arch/pc532/dev/ncrreg.h index e64f295cc1e7..87d981a82cf4 100644 --- a/sys/arch/pc532/dev/ncrreg.h +++ b/sys/arch/pc532/dev/ncrreg.h @@ -1,8 +1,7 @@ -/* $NetBSD: ncrreg.h,v 1.1 1995/06/09 04:36:26 phil Exp $ */ +/* $NetBSD: ncrreg.h,v 1.2 1995/08/25 07:30:40 phil Exp $ */ /* - * Copyright (c) 1995 Leo Weppelman. - * PC532-Port by Matthias Pfaller. + * Copyright (c) 1994 Matthias Pfaller. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -15,7 +14,7 @@ * 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 Leo Weppelman. + * This product includes software developed by Matthias Pfaller. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * @@ -29,117 +28,35 @@ * 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. + * + * $Id: ncrreg.h,v 1.2 1995/08/25 07:30:40 phil Exp $ */ -#ifndef _NCR5380REG_H -#define _NCR5380REG_H +#ifndef _NCRREG_H +#define _NCRREG_H #define PDMA_ADDRESS ((volatile u_char *) 0xffe00000) -#define SCSI_5380 ((volatile struct scsi_5380 *) 0xffd00000) +#define NCR5380 ((volatile struct ncr5380 *) 0xffd00000) -struct scsi_5380 { - volatile u_char scsi_5380[8]; /* use only the odd bytes */ +struct ncr5380 { + volatile u_char regs[8]; /* use only the odd bytes */ }; -#define scsi_data scsi_5380[0] /* Data register */ -#define scsi_icom scsi_5380[1] /* Initiator command register */ -#define scsi_mode scsi_5380[2] /* Mode register */ -#define scsi_tcom scsi_5380[3] /* Target command register */ -#define scsi_idstat scsi_5380[4] /* Bus status register */ -#define scsi_dmstat scsi_5380[5] /* DMA status register */ -#define scsi_trcv scsi_5380[6] /* Target receive register */ -#define scsi_ircv scsi_5380[7] /* Initiator receive register */ +#define ncr_data regs[0] /* Data register */ +#define ncr_icom regs[1] /* Initiator command register */ +#define ncr_mode regs[2] /* Mode register */ +#define ncr_tcom regs[3] /* Target command register */ +#define ncr_idstat regs[4] /* Bus status register */ +#define ncr_dmstat regs[5] /* DMA status register */ +#define ncr_trcv regs[6] /* Target receive register */ +#define ncr_ircv regs[7] /* Initiator receive register */ -/* - * Definitions for Initiator command register. - */ -#define SC_A_RST 0x80 /* RW - Assert RST */ -#define SC_TEST 0x40 /* W - Test mode */ -#define SC_AIP 0x40 /* R - Arbitration in progress */ -#define SC_LA 0x20 /* R - Lost arbitration */ -#define SC_A_ACK 0x10 /* RW - Assert ACK */ -#define SC_A_BSY 0x08 /* RW - Assert BSY */ -#define SC_A_SEL 0x04 /* RW - Assert SEL */ -#define SC_A_ATN 0x02 /* RW - Assert ATN */ -#define SC_ADTB 0x01 /* RW - Assert Data To Bus */ +#define GET_5380_REG(rnum) NCR5380->regs[rnum] +#define SET_5380_REG(rnum,val) (NCR5380->regs[rnum] = val) +#define scsi_ienable() intr_enable(IR_SCSI1) +#define scsi_idisable() intr_disable(IR_SCSI1) +#define scsi_clr_ipend() do { \ + int i = GET_5380_REG(NCR5380_IRCV); \ + } while (0) -/* - * Definitions for mode register - */ -#define SC_B_DMA 0x80 /* RW - Block mode DMA (not on TT!) */ -#define SC_T_MODE 0x40 /* RW - Target mode */ -#define SC_E_PAR 0x20 /* RW - Enable parity check */ -#define SC_E_PARI 0x10 /* RW - Enable parity interrupt */ -#define SC_E_EOPI 0x08 /* RW - Enable End Of Process Interrupt */ -#define SC_MON_BSY 0x04 /* RW - Monitor BSY */ -#define SC_M_DMA 0x02 /* RW - Set DMA mode */ -#define SC_ARBIT 0x01 /* RW - Arbitrate */ - -/* - * Definitions for tcom register - */ -#define SC_LBS 0x80 /* RW - Last Byte Send (not on TT!) */ -#define SC_A_REQ 0x08 /* RW - Assert REQ */ -#define SC_A_MSG 0x04 /* RW - Assert MSG */ -#define SC_A_CD 0x02 /* RW - Assert C/D */ -#define SC_A_IO 0x01 /* RW - Assert I/O */ - -/* - * Definitions for idstat register - */ -#define SC_S_RST 0x80 /* R - RST is set */ -#define SC_S_BSY 0x40 /* R - BSY is set */ -#define SC_S_REQ 0x20 /* R - REQ is set */ -#define SC_S_MSG 0x10 /* R - MSG is set */ -#define SC_S_CD 0x08 /* R - C/D is set */ -#define SC_S_IO 0x04 /* R - I/O is set */ -#define SC_S_SEL 0x02 /* R - SEL is set */ -#define SC_S_PAR 0x01 /* R - Parity bit */ - -/* - * Definitions for dmastat register - */ -#define SC_END_DMA 0x80 /* R - End of DMA */ -#define SC_DMA_REQ 0x40 /* R - DMA request */ -#define SC_PAR_ERR 0x20 /* R - Parity error */ -#define SC_IRQ_SET 0x10 /* R - IRQ is active */ -#define SC_PHS_MTCH 0x08 /* R - Phase Match */ -#define SC_BSY_ERR 0x04 /* R - Busy error */ -#define SC_ATN_STAT 0x02 /* R - State of ATN line */ -#define SC_ACK_STAT 0x01 /* R - State of ACK line */ -#define SC_S_SEND 0x00 /* W - Start DMA output */ - -#define SC_CLINT { /* Clear interrupts */ \ - int i = SCSI_5380->scsi_ircv; \ - } - - -/* - * Definition of SCSI-bus phases. The values are determined by signals - * on the SCSI-bus. DO NOT CHANGE! - * The values must be used to index the pointers in SCSI-PARMS. - */ -#define NR_PHASE 8 -#define PH_DATAOUT 0 -#define PH_DATAIN 1 -#define PH_CMD 2 -#define PH_STATUS 3 -#define PH_RES1 4 -#define PH_RES2 5 -#define PH_MSGOUT 6 -#define PH_MSGIN 7 - -#define PH_OUT(phase) (!(phase & 1)) /* TRUE if output phase */ -#define PH_IN(phase) (phase & 1) /* TRUE if input phase */ - -/* - * Id of Host-adapter - */ -#define SC_HOST_ID 0x80 - -/* - * Base setting for 5380 mode register - */ -#define IMODE_BASE SC_E_PAR - -#endif /* _NCR5380REG_H */ +#endif /* _NCRREG_H */ diff --git a/sys/arch/pc532/dev/oldncr.c b/sys/arch/pc532/dev/oldncr.c new file mode 100644 index 000000000000..2761d4f43dd0 --- /dev/null +++ b/sys/arch/pc532/dev/oldncr.c @@ -0,0 +1,953 @@ +/* $NetBSD: oldncr.c,v 1.1 1995/08/25 07:30:41 phil Exp $ */ + +/* + * Copyright (C) 1993 Allen K. Briggs, Chris P. Caputo, + * Michael L. Finch, Bradley A. Grantham, and + * Lawrence A. Kesteloot + * 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 the Alice Group. + * 4. The names of the Alice Group or any of its members may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``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 ALICE GROUP 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. + */ + +/* Modified for use with the pc532 by Phil Nelson, June 94. */ + +#define PSEUDO_DMA 1 + +static int ncr_debug=1; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ncr_defs.h" +#include "ncr_5380.h" + +#define NCR5380 DP8490 + +#define SCI_PHASE_DISC 0 /* sort of ... */ +#define SCI_CLR_INTR(regs) {register int temp = regs->sci_iack;} +#define SCI_ACK(ptr,phase) (ptr)->sci_tcmd = (phase) +#define SCSI_TIMEOUT_VAL 10000000 +#define WAIT_FOR_NOT_REQ(ptr) { \ + int scsi_timeout = SCSI_TIMEOUT_VAL; \ + while ( ((ptr)->sci_bus_csr & SCI_BUS_REQ) && \ + ((ptr)->sci_bus_csr & SCI_BUS_REQ) && \ + ((ptr)->sci_bus_csr & SCI_BUS_REQ) && \ + (--scsi_timeout) ); \ + if (!scsi_timeout) { \ + printf("scsi timeout--WAIT_FOR_NOT_REQ---ncr.c, \ + line %d.\n", __LINE__); \ + goto scsi_timeout_error; \ + } \ + } + +#define WAIT_FOR_REQ(ptr) { \ + int scsi_timeout = SCSI_TIMEOUT_VAL; \ + while ( (((ptr)->sci_bus_csr & SCI_BUS_REQ) == 0) && \ + (((ptr)->sci_bus_csr & SCI_BUS_REQ) == 0) && \ + (((ptr)->sci_bus_csr & SCI_BUS_REQ) == 0) && \ + (--scsi_timeout) ); \ + if (!scsi_timeout) { \ + printf("scsi timeout--WAIT_FOR_REQ---ncr.c, \ + line %d.\n", __LINE__); \ + goto scsi_timeout_error; \ + } \ + } +#define WAIT_FOR_BSY(ptr) { \ + int scsi_timeout = SCSI_TIMEOUT_VAL; \ + while ((((ptr)->sci_bus_csr & SCI_BUS_BSY) == 0) && \ + (((ptr)->sci_bus_csr & SCI_BUS_BSY) == 0) && \ + (((ptr)->sci_bus_csr & SCI_BUS_BSY) == 0) && \ + (--scsi_timeout) ); \ + if (!scsi_timeout) { \ + printf("scsi timeout--WAIT_FOR_BSY---ncr.c, \ + line %d.\n", __LINE__); \ + goto scsi_timeout_error; \ + } \ + } + +#ifdef DDB +int Debugger(); +#else +#define Debugger() panic("Should call Debugger here (mac/dev/ncr.c).") +#endif + +typedef unsigned long int physaddr; +typedef sci_regmap_t sci_padded_regmap_t; + +#define NNCR5380 1 + +struct ncr5380_softc { + struct device sc_dev; + struct scsi_link sc_link; +}; + +/* From the mapping of the pc532 address space. See pc532/machdep.c */ +static volatile sci_padded_regmap_t *ncr = (sci_regmap_t *) 0xffd00000; +static volatile long *sci_4byte_addr= (long *) 0xffe00000; +static volatile u_char *sci_1byte_addr=(u_char *) 0xffe00000; + +static u_int ncr5380_minphys(struct buf *bp); +static int ncr5380_scsi_cmd(struct scsi_xfer *xs); + +static int ncr5380_show_scsi_cmd(struct scsi_xfer *xs); +static int ncr5380_reset_target(int adapter, int target); +static int ncr5380_poll(int adapter, int timeout); +static int ncr5380_send_cmd(struct scsi_xfer *xs); + +extern void ncr5380_intr(int adapter); +extern void spinwait(int); + +static int scsi_gen(int adapter, int id, int lun, + struct scsi_generic *cmd, int cmdlen, + void *databuf, int datalen); +static int scsi_group0(int adapter, int id, int lun, + int opcode, int addr, int len, + int flags, caddr_t databuf, int datalen); + +static char scsi_name[] = "ncr"; + +struct scsi_adapter ncr5380_switch = { + ncr5380_scsi_cmd, /* scsi_cmd() */ + ncr5380_minphys, /* scsi_minphys() */ + 0, /* open_target_lu() */ + 0 /* close_target_lu() */ +}; + +/* This is copied from julian's bt driver */ +/* "so we have a default dev struct for our link struct." */ +struct scsi_device ncr_dev = { + NULL, /* Use default error handler. */ + NULL, /* have a queue, served by this (?) */ + NULL, /* have no async handler. */ + NULL /* Use default "done" routine. */ +}; + +extern int matchbyname(); +static int ncrprobe(); +static void ncrattach(); + +struct cfdriver ncrcd = + { NULL, "ncr", ncrprobe, ncrattach, + DV_DULL, sizeof(struct ncr5380_softc), NULL, 0 }; + +static int +ncrprobe(parent, self, aux) + struct device *parent, *self; + void *aux; +{ +/* int unit = cf->cf_unit; */ + struct ncr5380_softc *ncr5380 = (void *)self; + +#if 0 +DELETE THIS ???? + if (unit >= NNCR5380) { + printf("ncr5380attach: unit %d more than %d configured.\n", + unit+1, NNCR5380); + return 0; + } + ncr5380 = malloc(sizeof(struct ncr5380_data), M_TEMP, M_NOWAIT); + if (!ncr5380) { + printf("ncr5380attach: Can't malloc.\n"); + return 0; + } + + bzero(ncr5380, sizeof(*ncr5380)); + ncr5380data[unit] = ncr5380; +#endif + + /* If we call this, we need to add SPL_DP to the bio mask! */ + /* PL_bio |= SPL_DP; Not yet ... no interrupts */ + + return 1; +} + +static void +ncrattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + register volatile sci_padded_regmap_t *regs = ncr; + struct ncr5380_softc *ncr5380 = (void *)self; + int r; + + ncr5380->sc_link.adapter_softc = ncr5380; +/* ncr5380->sc_link.scsibus = 0; */ + ncr5380->sc_link.adapter_target = 7; + ncr5380->sc_link.adapter = &ncr5380_switch; + ncr5380->sc_link.device = &ncr_dev; + ncr5380->sc_link.openings = 1; + + printf("\n"); + + config_found(self, &(ncr5380->sc_link), NULL); +} + +#define MIN_PHYS 65536 /*BARF!!!!*/ +static u_int +ncr5380_minphys(struct buf *bp) +{ + if (bp->b_bcount > MIN_PHYS) { + printf("Uh-oh... ncr5380_minphys setting bp->b_bcount = %x.\n", MIN_PHYS); + bp->b_bcount = MIN_PHYS; + } + return (minphys(bp)); +} +#undef MIN_PHYS + +static int +ncr5380_scsi_cmd(struct scsi_xfer *xs) +{ + int flags, s, r; + + flags = xs->flags; + if (xs->bp) flags |= (SCSI_NOSLEEP); + if ( flags & ITSDONE ) { + printf("Already done?"); + xs->flags &= ~ITSDONE; + } + if ( ! ( flags & INUSE ) ) { + printf("Not in use?"); + xs->flags |= INUSE; + } + + if ( flags & SCSI_RESET ) { + printf("flags & SCSIRESET.\n"); + s = splbio(); + ncr5380_reset_target(xs->sc_link->scsibus, + xs->sc_link->target); + splx(s); + return(COMPLETE); + } + + xs->resid = 0; /* Default value? */ + r = ncr5380_send_cmd(xs); + xs->flags |= ITSDONE; + scsi_done(xs); + switch(r) { + case COMPLETE: case SUCCESSFULLY_QUEUED: + r = SUCCESSFULLY_QUEUED; + if (xs->flags&SCSI_POLL) + r = COMPLETE; + break; + default: + break; + } + return r; +} + +static int +ncr5380_show_scsi_cmd(struct scsi_xfer *xs) +{ + u_char *b = (u_char *) xs->cmd; + int i = 0; + + if ( ! ( xs->flags & SCSI_RESET ) ) { + printf("ncr5380(%d:%d:%d)-", + xs->sc_link->scsibus, xs->sc_link->target, xs->sc_link->lun); + while (i < xs->cmdlen) { + if (i) printf(","); + printf("%x",b[i++]); + } + printf("-\n"); + } else { + printf("ncr5380(%d:%d:%d)-RESET-\n", + xs->sc_link->scsibus, xs->sc_link->target, xs->sc_link->lun); + } +} + +/* + * Actual chip control. + */ + +void +delay(int timeo) +{ + int len; + for (len=0;lensci_mode = 0x00; +} + +extern int +scsi_irq_intr(void) +{ + register volatile sci_padded_regmap_t *regs = ncr; + +/* if (regs->sci_csr != SCI_CSR_PHASE_MATCH) + printf("scsi_irq_intr called (not just phase match -- " + "csr = 0x%x, bus_csr = 0x%x).\n", + regs->sci_csr, regs->sci_bus_csr); + ncr5380_intr(0); */ + return 1; +} + +extern int +scsi_drq_intr(void) +{ +/* printf("scsi_drq_intr called.\n"); */ +/* ncr5380_intr(0); */ + return 1; +} + +static int +ncr5380_reset_target(int adapter, int target) +{ + register volatile sci_padded_regmap_t *regs = ncr; + int dummy; + + scsi_select_ctlr (DP8490); + regs->sci_icmd = SCI_ICMD_TEST; + regs->sci_icmd = SCI_ICMD_TEST | SCI_ICMD_RST; + delay(2500); + regs->sci_icmd = 0; + + regs->sci_mode = 0; + regs->sci_tcmd = SCI_PHASE_DISC; + regs->sci_sel_enb = 0; + + SCI_CLR_INTR(regs); + SCI_CLR_INTR(regs); +} + + +static int +ncr5380_send_cmd(struct scsi_xfer *xs) +{ + int s, i, sense; + +#if 0 + ncr5380_show_scsi_cmd(xs); +#endif + s = splbio(); + sense = scsi_gen( xs->sc_link->scsibus, xs->sc_link->target, + xs->sc_link->lun, xs->cmd, xs->cmdlen, + xs->data, xs->datalen ); + splx(s); + switch (sense) { + case 0x00: + xs->error = XS_NOERROR; + return (COMPLETE); + case 0x02: /* Check condition */ + for (i = 10; i; i--) { + s = splbio(); + sense = scsi_group0(xs->sc_link->scsibus, + xs->sc_link->target, + xs->sc_link->lun, + 0x3, 0x0, + sizeof(struct scsi_sense_data), + 0, (caddr_t) &(xs->sense), + sizeof(struct scsi_sense_data)); + splx(s); + if (sense == 0) break; + if (sense == 8 + && (xs->flags & SCSI_NOSLEEP) == 0) + tsleep((caddr_t)&lbolt, PRIBIO, "ncrbusy", 0); + } + if (!i) + printf("ncr(%d:%d): Sense failed (dev busy)\n", + xs->sc_link->target, xs->sc_link->lun); + xs->error = XS_SENSE; + return COMPLETE; + case 0x08: /* Busy */ + xs->error = XS_BUSY; + return COMPLETE; + default: + xs->error = XS_DRIVER_STUFFUP; + return COMPLETE; + } +} + +static int +select_target(register volatile sci_padded_regmap_t *regs, + u_char myid, u_char tid, int with_atn) +{ + register u_char bid, icmd; + int ret = SCSI_RET_RETRY; + + if ((regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL)) && + (regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL)) && + (regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL))) + return ret; + + /* for our purposes.. */ + myid = 1 << myid; + tid = 1 << tid; + + regs->sci_sel_enb = 0; /*myid; we don't want any interrupts. */ + regs->sci_tcmd = 0; /* get into a harmless state */ + regs->sci_mode = 0; /* get into a harmless state */ + + regs->sci_odata = myid; + regs->sci_mode = SCI_MODE_ARB; +/* regs->sci_mode |= SCI_MODE_ARB; */ + /* AIP might not set if BSY went true after we checked */ + for (bid = 0; bid < 20; bid++) /* 20usec circa */ + if (regs->sci_icmd & SCI_ICMD_AIP) + break; + if ((regs->sci_icmd & SCI_ICMD_AIP) == 0) { + goto lost; + } + + spinwait(2); /* 2.2us arb delay */ + + if (regs->sci_icmd & SCI_ICMD_LST) { + goto lost; + } + + regs->sci_mode &= ~SCI_MODE_PAR_CHK; + bid = regs->sci_data; + + if ((bid & ~myid) > myid) { + goto lost; + } + if (regs->sci_icmd & SCI_ICMD_LST) { + goto lost; + } + + /* Won arbitration, enter selection phase now */ + icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST); + icmd |= (with_atn ? (SCI_ICMD_SEL|SCI_ICMD_ATN) : SCI_ICMD_SEL); + icmd |= SCI_ICMD_BSY; + regs->sci_icmd = icmd; + + if (regs->sci_icmd & SCI_ICMD_LST) { + goto nosel; + } + + /* XXX a target that violates specs might still drive the bus XXX */ + /* XXX should put our id out, and after the delay check nothi XXX */ + /* XXX ng else is out there. XXX */ + + delay(0); + + regs->sci_tcmd = 0; + regs->sci_odata = myid | tid; + regs->sci_sel_enb = 0; + +/* regs->sci_mode &= ~SCI_MODE_ARB; 2 deskew delays, too */ + regs->sci_mode = 0; /* 2 deskew delays, too */ + + icmd |= SCI_ICMD_DATA; + icmd &= ~(SCI_ICMD_BSY); + + regs->sci_icmd = icmd; + + /* bus settle delay, 400ns */ + delay(2); /* too much (was 2) ? */ + +/* regs->sci_mode |= SCI_MODE_PAR_CHK; */ + + { + register int timeo = 2500;/* 250 msecs in 100 usecs chunks */ + while ((regs->sci_bus_csr & SCI_BUS_BSY) == 0) { + if (--timeo > 0) { + delay(100); + } else { + goto nodev; + } + } + } + + icmd &= ~(SCI_ICMD_DATA|SCI_ICMD_SEL); + regs->sci_icmd = icmd; +/* regs->sci_sel_enb = myid;*/ /* looks like we should NOT have it */ + return SCSI_RET_SUCCESS; +nodev: + ret = SCSI_RET_DEVICE_DOWN; + regs->sci_sel_enb = myid; +nosel: + icmd &= ~(SCI_ICMD_DATA|SCI_ICMD_SEL|SCI_ICMD_ATN); + regs->sci_icmd = icmd; +lost: + regs->sci_mode = 0; + + return ret; +} + +sci_data_out(regs, phase, count, data) + register sci_padded_regmap_t *regs; + unsigned char *data; +{ + register unsigned char icmd; + register int cnt=0; + + /* ..checks.. */ + + icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST); +loop: + if (SCI_CUR_PHASE(regs->sci_bus_csr) != phase) + return cnt; + + WAIT_FOR_REQ(regs); + icmd |= SCI_ICMD_DATA; + regs->sci_icmd = icmd; + regs->sci_odata = *data++; + icmd |= SCI_ICMD_ACK; + regs->sci_icmd = icmd; + + icmd &= ~(SCI_ICMD_DATA|SCI_ICMD_ACK); + WAIT_FOR_NOT_REQ(regs); + regs->sci_icmd = icmd; + ++cnt; + if (--count > 0) + goto loop; +scsi_timeout_error: + return cnt; +} + +sci_data_in(regs, phase, count, data) + register sci_padded_regmap_t *regs; + unsigned char *data; +{ + register unsigned char icmd; + register int cnt=0; + + /* ..checks.. */ + + icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST); + +loop: + if (SCI_CUR_PHASE(regs->sci_bus_csr) != phase) + return cnt; + + WAIT_FOR_REQ(regs); + *data++ = regs->sci_data; + icmd |= SCI_ICMD_ACK; + regs->sci_icmd = icmd; + + icmd &= ~SCI_ICMD_ACK; + WAIT_FOR_NOT_REQ(regs); + regs->sci_icmd = icmd; + ++cnt; + if (--count > 0) + goto loop; + +scsi_timeout_error: + return cnt; +} + +static int +command_transfer(register volatile sci_padded_regmap_t *regs, + int maxlen, u_char *data, u_char *status, u_char *msg) +{ + int xfer=0, phase; + +/* printf("command_transfer called for 0x%x.\n", *data); */ + + regs->sci_icmd = 0; + + while (1) { + + WAIT_FOR_REQ(regs); + + phase = SCI_CUR_PHASE(regs->sci_bus_csr); + + switch (phase) { + case SCSI_PHASE_CMD: + SCI_ACK(regs,SCSI_PHASE_CMD); + xfer += sci_data_out(regs, SCSI_PHASE_CMD, + maxlen, data); + return xfer; + case SCSI_PHASE_DATA_IN: + printf("Data in phase in command_transfer?\n"); + return 0; + case SCSI_PHASE_DATA_OUT: + printf("Data out phase in command_transfer?\n"); + return 0; + case SCSI_PHASE_STATUS: + SCI_ACK(regs,SCSI_PHASE_STATUS); + printf("status in command_transfer.\n"); + sci_data_in(regs, SCSI_PHASE_STATUS, + 1, status); + break; + case SCSI_PHASE_MESSAGE_IN: + SCI_ACK(regs,SCSI_PHASE_MESSAGE_IN); + printf("msgin in command_transfer.\n"); + sci_data_in(regs, SCSI_PHASE_MESSAGE_IN, + 1, msg); + break; + case SCSI_PHASE_MESSAGE_OUT: + SCI_ACK(regs,SCSI_PHASE_MESSAGE_OUT); + sci_data_out(regs, SCSI_PHASE_MESSAGE_OUT, + 1, msg); + break; + default: + printf("Unexpected phase 0x%x in " + "command_transfer().\n", phase); +scsi_timeout_error: + return xfer; + break; + } + } +} + +static int +data_transfer(register volatile sci_padded_regmap_t *regs, + int maxlen, u_char *data, u_char *status, u_char *msg) +{ + int retlen = 0, xfer, phase; + + regs->sci_icmd = 0; + + *status = 0; + + while (1) { + + WAIT_FOR_REQ(regs); + + phase = SCI_CUR_PHASE(regs->sci_bus_csr); + + switch (phase) { + case SCSI_PHASE_CMD: + printf("Command phase in data_transfer().\n"); + return retlen; + case SCSI_PHASE_DATA_IN: + SCI_ACK(regs,SCSI_PHASE_DATA_IN); +#if PSEUDO_DMA + xfer = sci_pdma_in(regs, SCSI_PHASE_DATA_IN, + maxlen, data); +#else + xfer = sci_data_in(regs, SCSI_PHASE_DATA_IN, + maxlen, data); +#endif + retlen += xfer; + maxlen -= xfer; + break; + case SCSI_PHASE_DATA_OUT: + SCI_ACK(regs,SCSI_PHASE_DATA_OUT); +#if PSEUDO_DMA + xfer = sci_pdma_out(regs, SCSI_PHASE_DATA_OUT, + maxlen, data); +#else + xfer = sci_data_out(regs, SCSI_PHASE_DATA_OUT, + maxlen, data); +#endif + retlen += xfer; + maxlen -= xfer; + break; + case SCSI_PHASE_STATUS: + SCI_ACK(regs,SCSI_PHASE_STATUS); + sci_data_in(regs, SCSI_PHASE_STATUS, + 1, status); + break; + case SCSI_PHASE_MESSAGE_IN: + SCI_ACK(regs,SCSI_PHASE_MESSAGE_IN); + sci_data_in(regs, SCSI_PHASE_MESSAGE_IN, + 1, msg); + if (*msg == 0) { + return retlen; + } else { + printf( "message 0x%x in " + "data_transfer.\n", *msg); + } + break; + case SCSI_PHASE_MESSAGE_OUT: + SCI_ACK(regs,SCSI_PHASE_MESSAGE_OUT); + sci_data_out(regs, SCSI_PHASE_MESSAGE_OUT, + 1, msg); + break; + default: + printf( "Unexpected phase 0x%x in " + "data_transfer().\n", phase); +scsi_timeout_error: + return retlen; + break; + } + } +} + +static int +scsi_request(register volatile sci_padded_regmap_t *regs, + int target, int lun, u_char *cmd, int cmdlen, + char *databuf, int datalen, int *sent, int *ret) +{ +/* Returns 0 on success, -1 on internal error, or the status byte */ + int cmd_bytes_sent, r; + u_char stat, msg, c; + + *sent = 0; + scsi_select_ctlr (DP8490); + + if ( ( r = select_target(regs, 7, target, 1) ) != SCSI_RET_SUCCESS) { + *ret = r; + SCI_CLR_INTR(regs); + switch (r) { + case SCSI_RET_RETRY: + return 0x08; + default: + printf("select_target(target %d, lun %d) failed(%d).\n", + target, lun, r); + case SCSI_RET_DEVICE_DOWN: + return -1; + } + } + + c = 0x80 | lun; + + if ((cmd_bytes_sent = command_transfer(regs, cmdlen, + (u_char *) cmd, &stat, &c)) + != cmdlen) { + SCI_CLR_INTR(regs); + *ret = SCSI_RET_COMMAND_FAIL; + printf("Data underrun sending CCB (%d bytes of %d, sent).\n", + cmd_bytes_sent, cmdlen); + return -1; + } + + *sent=data_transfer(regs, datalen, (u_char *)databuf, + &stat, &msg); + + *ret = 0; + + return stat; +} + +static int +scsi_gen(int adapter, int id, int lun, struct scsi_generic *cmd, + int cmdlen, void *databuf, int datalen) +{ + register volatile sci_padded_regmap_t *regs = ncr; + int i,j,sent,ret; + + cmd->bytes[0] |= ((u_char) lun << 5); + + i = scsi_request(regs, id, lun, (u_char *) cmd, cmdlen, + databuf, datalen, &sent, &ret); + + return i; +} + +static int +scsi_group0(int adapter, int id, int lun, int opcode, int addr, int len, + int flags, caddr_t databuf, int datalen) +{ + register volatile sci_padded_regmap_t *regs = ncr; + unsigned char cmd[6]; + int i,j,sent,ret; + + cmd[0] = opcode; /* Operation code */ + cmd[1] = (lun << 5) | ((addr >> 16) & 0x1F); /* Lun & MSB of addr */ + cmd[2] = (addr >> 8) & 0xFF; /* addr */ + cmd[3] = addr & 0xFF; /* LSB of addr */ + cmd[4] = len; /* Allocation length */ + cmd[5] = flags; /* Link/Flag */ + + i = scsi_request(regs, id, lun, cmd, 6, databuf, datalen, &sent, &ret); + + return i; +} + +/* pseudo-dma action */ + +#if PSEUDO_DMA + +#define TIMEOUT 1000000 +#define READY(poll) \ + i = TIMEOUT; \ + while ((regs->sci_csr & (SCI_CSR_DREQ|SCI_CSR_PHASE_MATCH)) \ + !=(SCI_CSR_DREQ|SCI_CSR_PHASE_MATCH)) \ + if ( !(regs->sci_csr & SCI_CSR_PHASE_MATCH) \ + || !(regs->sci_bus_csr & SCI_BUS_BSY) \ + || (i-- < 0) ) { \ + printf("ncr.c: timeout counter = %d, len = %d count=%d (count-len %d).\n", \ + i, len,count,count-len); \ + printf("ncr_debug = %d, 1=out, 2=in",ncr_debug); \ + /*dump_regs();*/ \ + if (poll && !(regs->sci_csr & SCI_CSR_PHASE_MATCH)) { \ + regs->sci_icmd &= ~SCI_ICMD_DATA; \ + len--; \ + } else { \ + regs->sci_mode &= ~SCI_MODE_DMA; \ + } \ + return count-len; \ + } + +#define W1 *byte_data = *data++ +#define W4 *long_data = *((long*)data)++ + +sci_pdma_out(regs, phase, count, data) + register volatile sci_padded_regmap_t *regs; + int phase; + int count; + u_char *data; +{ + register volatile long *long_data = sci_4byte_addr; + register volatile u_char *byte_data = sci_1byte_addr; + register int len = count, i; + +ncr_debug=1; + + if (count < 128) + return sci_data_out(regs, phase, count, data); + + WAIT_FOR_BSY(regs); + regs->sci_mode |= SCI_MODE_DMA; + regs->sci_icmd |= SCI_ICMD_DATA; + regs->sci_dma_send = 0; + + while ( len >= 64 ) { + READY(1); W1; READY(1); W1; READY(1); W1; READY(1); W1; + READY(1); + W4;W4;W4; W4;W4;W4;W4; W4;W4;W4;W4; W4;W4;W4;W4; + len -= 64; + } + while (len) { + READY(1); + W1; + len--; + } + i = TIMEOUT; + while ( ((regs->sci_csr & (SCI_CSR_DREQ|SCI_CSR_PHASE_MATCH)) + == SCI_CSR_PHASE_MATCH) && --i); + if (!i) + printf("ncr.c:%d: timeout waiting for SCI_CSR_DREQ.\n", __LINE__); + *byte_data = 0; +scsi_timeout_error: + regs->sci_mode &= ~SCI_MODE_DMA; + return count-len; +} + +#undef W1 +#undef W4 + +#define R4 *((long *)data)++ = *long_data +#define R1 *data++ = *byte_data + +sci_pdma_in(regs, phase, count, data) + register volatile sci_padded_regmap_t *regs; + int phase; + int count; + u_char *data; +{ + register volatile long *long_data = sci_4byte_addr; + register volatile u_char *byte_data = sci_1byte_addr; + register int len = count, i; + +ncr_debug=2; + if (count < 128) + return sci_data_in(regs, phase, count, data); + +/* printf("Called sci_pdma_in(0x%x, 0x%x, %d, 0x%x.\n", regs, phase, count, data); */ + + WAIT_FOR_BSY(regs); + regs->sci_mode |= SCI_MODE_DMA; + regs->sci_icmd |= SCI_ICMD_DATA; + regs->sci_irecv = 0; + + while (len >= 1024) { + READY(0); + R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; + R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; /* 128 */ + READY(0); + R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; + R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; /* 256 */ + READY(0); + R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; + R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; /* 384 */ + READY(0); + R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; + R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; /* 512 */ + READY(0); + R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; + R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; /* 640 */ + READY(0); + R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; + R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; /* 768 */ + READY(0); + R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; + R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; /* 896 */ + READY(0); + R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; + R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; /*1024 */ + len -= 1024; + } + while (len >= 128) { + READY(0); + R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; + R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; R4;R4;R4;R4; /* 128 */ + len -= 128; + } + while (len) { + READY(0); + R1; + len--; + } +scsi_timeout_error: + regs->sci_mode &= ~SCI_MODE_DMA; + return count - len; +} +#undef R4 +#undef R1 +#endif + +/* Some stuff from dp.c ... */ + + +#if 0 +/* Select a SCSI device. + */ +int scsi_select_ctlr (int ctlr) +{ + /* May need other stuff here to syncronize between dp & aic. */ + + RD_ADR (u_char, ICU_IO) &= ~ICU_SCSI_BIT; /* i/o, not port */ + RD_ADR (u_char, ICU_DIR) &= ~ICU_SCSI_BIT; /* output */ + if (ctlr == NCR5380) + RD_ADR (u_char, ICU_DATA) &= ~ICU_SCSI_BIT; /* select = 0 for 8490 */ + else + RD_ADR (u_char, ICU_DATA) |= ICU_SCSI_BIT; /* select = 1 for AIC6250 */ +} +#endif diff --git a/sys/arch/pc532/dev/scn.c b/sys/arch/pc532/dev/scn.c index 6af7a6bf79fa..776fe70862bd 100644 --- a/sys/arch/pc532/dev/scn.c +++ b/sys/arch/pc532/dev/scn.c @@ -1,6 +1,6 @@ -/* $NetBSD: scn.c,v 1.18 1995/06/09 04:36:30 phil Exp $ */ +/* $NetBSD: scn.c,v 1.19 1995/08/25 07:30:42 phil Exp $ */ -/*- +/* * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * @@ -400,7 +400,7 @@ scnattach(parent, self, aux) /* Set up the interrupts. */ rs->uart->imr_int_bits - |= (/*IMR_RX_INT | IMR_TX_INT |*/ IMR_BRK_INT) << (4*rs->a_or_b) | IMR_IP_INT; + |= (/*IMR_RX_INT | IMR_TX_INT |*/ unit?IMR_BRK_INT:0) << (4*rs->a_or_b) | IMR_IP_INT; WR_ADR (u_char, rs->uart->imr_port, rs->uart->imr_int_bits); splx(x); @@ -640,7 +640,7 @@ scnintr(int line1) u_char rs_stat; u_char rs_ipcr; u_char ch; - + while (rs_work) { /* Loop to pick up ALL pending interrupts for device. */ @@ -663,11 +663,22 @@ scnintr(int line1) rs0->lstatus = RD_ADR(u_char, rs0->stat_port); if (rs0->lstatus & SR_BREAK) { ++rs0->break_interrupts; + RD_ADR(u_char, rs0->recv_port); /* Toss zero character. */ + rs_stat &= ~IMR_RX_INT; } - RD_ADR(u_char, rs0->recv_port); /* Toss zero character. */ - rs_stat &= ~IMR_RX_INT; WR_ADR (u_char, rs0->cmd_port, CMD_RESET_BRK); rs_work = TRUE; + if (line1 == 1 && (rs0->lstatus & SR_BREAK)) { + char c; + printf("\r\nDo you wont a dump (y/n)? "); + do + c = cngetc(); + while (c != 'y' && c != 'n'); + printf("%c\r\n", c); + if (c == 'y') { + panic("Panic Button"); + } + } } if (rs_stat & IMR_RX_INT && (tp0 != NULL)) { ch = RD_ADR(u_char, rs0->recv_port); @@ -691,10 +702,10 @@ scnintr(int line1) rs1->lstatus = RD_ADR(u_char, rs1->stat_port); if (rs1->lstatus & SR_BREAK) { ++rs1->break_interrupts; + RD_ADR(u_char, rs1->recv_port); /* Toss zero character. */ + rs_stat &= ~IMR_RXB_INT; } - RD_ADR(u_char, rs1->recv_port); /* Toss zero character. */ WR_ADR (u_char, rs1->cmd_port, CMD_RESET_BRK); - rs_stat &= ~IMR_RXB_INT; rs_work = TRUE; } if (rs_stat & IMR_RXB_INT && (tp1 != NULL)) { @@ -1039,7 +1050,8 @@ char scncngetc(dev_t dev) { char c; - int x=splhigh(); + int x = spltty(); + WR_ADR (u_char, SCN_FIRST_MAP_ADR + 14, (RTS_BIT | DTR_BIT)); while (0 == (RD_ADR (u_char, SCN_CON_MAP_STAT) & SR_RX_RDY)); c = RD_ADR(u_char, SCN_CON_MAP_DATA); splx(x); @@ -1064,7 +1076,7 @@ int ___lines = 0; scncnputc (dev_t dev, char c) { - int x = splhigh(); + int x = spltty(); if (c == '\n') scncnputc(dev,'\r'); if (_mapped) {