152889Sbostic /*- 252889Sbostic * Copyright (c) 1992 The Regents of the University of California. 352889Sbostic * All rights reserved. 452889Sbostic * 552889Sbostic * This code is derived from software contributed to Berkeley by 656819Sralph * Ralph Campbell and Rick Macklem. 752889Sbostic * 852889Sbostic * %sccs.include.redist.c% 952889Sbostic * 10*58658Sralph * @(#)asc.c 7.11 (Berkeley) 03/13/93 1152889Sbostic */ 1252889Sbostic 1352889Sbostic /* 1452889Sbostic * Mach Operating System 1552889Sbostic * Copyright (c) 1991,1990,1989 Carnegie Mellon University 1652889Sbostic * All Rights Reserved. 1752889Sbostic * 1852889Sbostic * Permission to use, copy, modify and distribute this software and its 1952889Sbostic * documentation is hereby granted, provided that both the copyright 2052889Sbostic * notice and this permission notice appear in all copies of the 2152889Sbostic * software, derivative works or modified versions, and any portions 2252889Sbostic * thereof, and that both notices appear in supporting documentation. 2352889Sbostic * 2452889Sbostic * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 2552889Sbostic * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 2652889Sbostic * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 2752889Sbostic * 2852889Sbostic * Carnegie Mellon requests users of this software to return to 2952889Sbostic * 3052889Sbostic * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 3152889Sbostic * School of Computer Science 3252889Sbostic * Carnegie Mellon University 3352889Sbostic * Pittsburgh PA 15213-3890 3452889Sbostic * 3552889Sbostic * any improvements or extensions that they make and grant Carnegie the 3652889Sbostic * rights to redistribute these changes. 3752889Sbostic */ 3852889Sbostic 3952889Sbostic /* 4052889Sbostic * HISTORY 4152889Sbostic * $Log: scsi_53C94_hdw.c,v $ 4252889Sbostic * Revision 2.5 91/02/05 17:45:07 mrt 4352889Sbostic * Added author notices 4452889Sbostic * [91/02/04 11:18:43 mrt] 4552889Sbostic * 4652889Sbostic * Changed to use new Mach copyright 4752889Sbostic * [91/02/02 12:17:20 mrt] 4852889Sbostic * 4952889Sbostic * Revision 2.4 91/01/08 15:48:24 rpd 5052889Sbostic * Added continuation argument to thread_block. 5152889Sbostic * [90/12/27 rpd] 5252889Sbostic * 5352889Sbostic * Revision 2.3 90/12/05 23:34:48 af 5452889Sbostic * Recovered from pmax merge.. and from the destruction of a disk. 5552889Sbostic * [90/12/03 23:40:40 af] 5652889Sbostic * 5752889Sbostic * Revision 2.1.1.1 90/11/01 03:39:09 af 5852889Sbostic * Created, from the DEC specs: 5952889Sbostic * "PMAZ-AA TURBOchannel SCSI Module Functional Specification" 6052889Sbostic * Workstation Systems Engineering, Palo Alto, CA. Aug 27, 1990. 6152889Sbostic * And from the NCR data sheets 6252889Sbostic * "NCR 53C94, 53C95, 53C96 Advances SCSI Controller" 6352889Sbostic * [90/09/03 af] 6452889Sbostic */ 6552889Sbostic 6652889Sbostic /* 6752889Sbostic * File: scsi_53C94_hdw.h 6852889Sbostic * Author: Alessandro Forin, Carnegie Mellon University 6952889Sbostic * Date: 9/90 7052889Sbostic * 7152889Sbostic * Bottom layer of the SCSI driver: chip-dependent functions 7252889Sbostic * 7352889Sbostic * This file contains the code that is specific to the NCR 53C94 7452889Sbostic * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start 7552889Sbostic * operation, and interrupt routine. 7652889Sbostic */ 7752889Sbostic 7852889Sbostic /* 7952889Sbostic * This layer works based on small simple 'scripts' that are installed 8052889Sbostic * at the start of the command and drive the chip to completion. 8152889Sbostic * The idea comes from the specs of the NCR 53C700 'script' processor. 8252889Sbostic * 8352889Sbostic * There are various reasons for this, mainly 8452889Sbostic * - Performance: identify the common (successful) path, and follow it; 8552889Sbostic * at interrupt time no code is needed to find the current status 8652889Sbostic * - Code size: it should be easy to compact common operations 8752889Sbostic * - Adaptability: the code skeleton should adapt to different chips without 8852889Sbostic * terrible complications. 8952889Sbostic * - Error handling: and it is easy to modify the actions performed 9052889Sbostic * by the scripts to cope with strange but well identified sequences 9152889Sbostic * 9252889Sbostic */ 9352889Sbostic 9456819Sralph #include <asc.h> 9552889Sbostic #if NASC > 0 9652889Sbostic 9756522Sbostic #include <sys/param.h> 9856522Sbostic #include <sys/systm.h> 9956819Sralph #include <sys/dkstat.h> 10056819Sralph #include <sys/buf.h> 10156819Sralph #include <sys/conf.h> 10256522Sbostic #include <sys/errno.h> 10352889Sbostic 10456819Sralph #include <machine/machConst.h> 10556819Sralph 10656525Sbostic #include <pmax/dev/device.h> 10756525Sbostic #include <pmax/dev/scsi.h> 10856525Sbostic #include <pmax/dev/ascreg.h> 10952889Sbostic 11056819Sralph #include <pmax/pmax/asic.h> 11156819Sralph #include <pmax/pmax/kmin.h> 11256819Sralph #include <pmax/pmax/pmaxtype.h> 11356819Sralph 11456819Sralph #define readback(a) { register int foo; foo = (a); } 11556819Sralph extern int pmax_boardtype; 11656819Sralph 11756819Sralph /* 11856819Sralph * In 4ns ticks. 11956819Sralph */ 12052889Sbostic int asc_to_scsi_period[] = { 12156819Sralph 32, 12256819Sralph 33, 12356819Sralph 34, 12456819Sralph 35, 12556819Sralph 5, 12656819Sralph 5, 12756819Sralph 6, 12856819Sralph 7, 12956819Sralph 8, 13056819Sralph 9, 13156819Sralph 10, 13256819Sralph 11, 13356819Sralph 12, 13456819Sralph 13, 13556819Sralph 14, 13656819Sralph 15, 13756819Sralph 16, 13856819Sralph 17, 13956819Sralph 18, 14056819Sralph 19, 14156819Sralph 20, 14256819Sralph 21, 14356819Sralph 22, 14456819Sralph 23, 14556819Sralph 24, 14656819Sralph 25, 14756819Sralph 26, 14856819Sralph 27, 14956819Sralph 28, 15056819Sralph 29, 15156819Sralph 30, 15256819Sralph 31, 15352889Sbostic }; 15452889Sbostic 15552889Sbostic /* 15652889Sbostic * Internal forward declarations. 15752889Sbostic */ 15852889Sbostic static void asc_reset(); 15952889Sbostic static void asc_startcmd(); 16052889Sbostic 16152889Sbostic #ifdef DEBUG 16252889Sbostic int asc_debug = 1; 16352889Sbostic int asc_debug_cmd; 16452889Sbostic int asc_debug_bn; 16552889Sbostic int asc_debug_sz; 166*58658Sralph #define NLOG 32 16752889Sbostic struct asc_log { 16852889Sbostic u_int status; 16952889Sbostic u_char state; 17052889Sbostic u_char msg; 17152889Sbostic int target; 172*58658Sralph int resid; 17352889Sbostic } asc_log[NLOG], *asc_logp = asc_log; 17452889Sbostic #define PACK(unit, status, ss, ir) \ 17552889Sbostic ((unit << 24) | (status << 16) | (ss << 8) | ir) 17652889Sbostic #endif 17752889Sbostic 17852889Sbostic /* 17952889Sbostic * Scripts are entries in a state machine table. 18052889Sbostic * A script has four parts: a pre-condition, an action, a command to the chip, 18152889Sbostic * and an index into asc_scripts for the next state. The first triggers error 18252889Sbostic * handling if not satisfied and in our case it is formed by the 18352889Sbostic * values of the interrupt register and status register, this 18452889Sbostic * basically captures the phase of the bus and the TC and BS 18552889Sbostic * bits. The action part is just a function pointer, and the 18652889Sbostic * command is what the 53C94 should be told to do at the end 18752889Sbostic * of the action processing. This command is only issued and the 18852889Sbostic * script proceeds if the action routine returns TRUE. 18952889Sbostic * See asc_intr() for how and where this is all done. 19052889Sbostic */ 19152889Sbostic typedef struct script { 19252889Sbostic int condition; /* expected state at interrupt time */ 19352889Sbostic int (*action)(); /* extra operations */ 19452889Sbostic int command; /* command to the chip */ 19552889Sbostic struct script *next; /* index into asc_scripts for next state */ 19652889Sbostic } script_t; 19752889Sbostic 19852889Sbostic /* Matching on the condition value */ 199*58658Sralph #define SCRIPT_MATCH(ir, csr) ((ir) | (((csr) & 0x67) << 8)) 20052889Sbostic 20152889Sbostic /* forward decls of script actions */ 20256819Sralph static int script_nop(); /* when nothing needed */ 20356819Sralph static int asc_end(); /* all come to an end */ 20456819Sralph static int asc_get_status(); /* get status from target */ 20556819Sralph static int asc_dma_in(); /* start reading data from target */ 20656819Sralph static int asc_last_dma_in(); /* cleanup after all data is read */ 20756819Sralph static int asc_resume_in(); /* resume data in after a message */ 20856819Sralph static int asc_resume_dma_in(); /* resume DMA after a disconnect */ 20956819Sralph static int asc_dma_out(); /* send data to target via dma */ 21056819Sralph static int asc_last_dma_out(); /* cleanup after all data is written */ 21156819Sralph static int asc_resume_out(); /* resume data out after a message */ 21256819Sralph static int asc_resume_dma_out(); /* resume DMA after a disconnect */ 21356819Sralph static int asc_sendsync(); /* negotiate sync xfer */ 21456819Sralph static int asc_replysync(); /* negotiate sync xfer */ 21556819Sralph static int asc_msg_in(); /* process a message byte */ 21656819Sralph static int asc_disconnect(); /* process an expected disconnect */ 21752889Sbostic 21852889Sbostic /* Define the index into asc_scripts for various state transitions */ 21952889Sbostic #define SCRIPT_DATA_IN 0 22052942Sralph #define SCRIPT_CONTINUE_IN 2 22152942Sralph #define SCRIPT_DATA_OUT 3 22252942Sralph #define SCRIPT_CONTINUE_OUT 5 22352942Sralph #define SCRIPT_SIMPLE 6 22452942Sralph #define SCRIPT_GET_STATUS 7 22552942Sralph #define SCRIPT_MSG_IN 9 22652942Sralph #define SCRIPT_REPLY_SYNC 11 22752889Sbostic #define SCRIPT_TRY_SYNC 12 22852942Sralph #define SCRIPT_DISCONNECT 15 22952942Sralph #define SCRIPT_RESEL 16 23052942Sralph #define SCRIPT_RESUME_IN 17 23152942Sralph #define SCRIPT_RESUME_DMA_IN 18 23252942Sralph #define SCRIPT_RESUME_OUT 19 23352942Sralph #define SCRIPT_RESUME_DMA_OUT 20 23452942Sralph #define SCRIPT_RESUME_NO_DATA 21 23552889Sbostic 23652889Sbostic /* 23752889Sbostic * Scripts 23852889Sbostic */ 23952889Sbostic script_t asc_scripts[] = { 24052942Sralph /* start data in */ 24152889Sbostic {SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_DATAI), /* 0 */ 24252889Sbostic asc_dma_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA, 24352942Sralph &asc_scripts[SCRIPT_DATA_IN + 1]}, 24452889Sbostic {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_STATUS), /* 1 */ 24552889Sbostic asc_last_dma_in, ASC_CMD_I_COMPLETE, 24652942Sralph &asc_scripts[SCRIPT_GET_STATUS]}, 24752889Sbostic 24858570Sralph /* continue data in after a chunk is finished */ 24952942Sralph {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAI), /* 2 */ 25052942Sralph asc_dma_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA, 25152942Sralph &asc_scripts[SCRIPT_DATA_IN + 1]}, 25252942Sralph 25352942Sralph /* start data out */ 25452942Sralph {SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_DATAO), /* 3 */ 25552889Sbostic asc_dma_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA, 25652942Sralph &asc_scripts[SCRIPT_DATA_OUT + 1]}, 25752942Sralph {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_STATUS), /* 4 */ 25852889Sbostic asc_last_dma_out, ASC_CMD_I_COMPLETE, 25952942Sralph &asc_scripts[SCRIPT_GET_STATUS]}, 26052889Sbostic 26158570Sralph /* continue data out after a chunk is finished */ 26252942Sralph {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAO), /* 5 */ 26352942Sralph asc_dma_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA, 26452942Sralph &asc_scripts[SCRIPT_DATA_OUT + 1]}, 26552942Sralph 26652889Sbostic /* simple command with no data transfer */ 26752942Sralph {SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_STATUS), /* 6 */ 26852889Sbostic script_nop, ASC_CMD_I_COMPLETE, 26952942Sralph &asc_scripts[SCRIPT_GET_STATUS]}, 27052889Sbostic 27152889Sbostic /* get status and finish command */ 27252942Sralph {SCRIPT_MATCH(ASC_INT_FC, ASC_PHASE_MSG_IN), /* 7 */ 27352889Sbostic asc_get_status, ASC_CMD_MSG_ACPT, 27452942Sralph &asc_scripts[SCRIPT_GET_STATUS + 1]}, 27552942Sralph {SCRIPT_MATCH(ASC_INT_DISC, 0), /* 8 */ 27652889Sbostic asc_end, ASC_CMD_NOP, 27752942Sralph &asc_scripts[SCRIPT_GET_STATUS + 1]}, 27852889Sbostic 27952889Sbostic /* message in */ 28052942Sralph {SCRIPT_MATCH(ASC_INT_FC, ASC_PHASE_MSG_IN), /* 9 */ 28152942Sralph asc_msg_in, ASC_CMD_MSG_ACPT, 28252942Sralph &asc_scripts[SCRIPT_MSG_IN + 1]}, 28352942Sralph {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_MSG_IN), /* 10 */ 28452889Sbostic script_nop, ASC_CMD_XFER_INFO, 28552942Sralph &asc_scripts[SCRIPT_MSG_IN]}, 28652889Sbostic 28752889Sbostic /* send synchonous negotiation reply */ 28852942Sralph {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_MSG_OUT), /* 11 */ 28952889Sbostic asc_replysync, ASC_CMD_XFER_INFO, 29052942Sralph &asc_scripts[SCRIPT_REPLY_SYNC]}, 29152889Sbostic 29252889Sbostic /* try to negotiate synchonous transfer parameters */ 29352889Sbostic {SCRIPT_MATCH(ASC_INT_FC | ASC_INT_BS, ASC_PHASE_MSG_OUT), /* 12 */ 29452889Sbostic asc_sendsync, ASC_CMD_XFER_INFO, 29553080Sralph &asc_scripts[SCRIPT_TRY_SYNC + 1]}, 29653080Sralph {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_MSG_IN), /* 13 */ 29752889Sbostic script_nop, ASC_CMD_XFER_INFO, 29853080Sralph &asc_scripts[SCRIPT_MSG_IN]}, 29953080Sralph {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_COMMAND), /* 14 */ 30053080Sralph script_nop, ASC_CMD_XFER_INFO | ASC_CMD_DMA, 30153080Sralph &asc_scripts[SCRIPT_RESUME_NO_DATA]}, 30252889Sbostic 30352942Sralph /* handle a disconnect */ 30452942Sralph {SCRIPT_MATCH(ASC_INT_DISC, ASC_PHASE_DATAO), /* 15 */ 30552942Sralph asc_disconnect, ASC_CMD_ENABLE_SEL, 30652942Sralph &asc_scripts[SCRIPT_RESEL]}, 30752942Sralph 30852889Sbostic /* reselect sequence: this is just a placeholder so match fails */ 30952942Sralph {SCRIPT_MATCH(0, ASC_PHASE_MSG_IN), /* 16 */ 31052889Sbostic script_nop, ASC_CMD_MSG_ACPT, 31152942Sralph &asc_scripts[SCRIPT_RESEL]}, 31252889Sbostic 31352942Sralph /* resume data in after a message */ 31452942Sralph {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAI), /* 17 */ 31552942Sralph asc_resume_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA, 31652942Sralph &asc_scripts[SCRIPT_DATA_IN + 1]}, 31752942Sralph 31852942Sralph /* resume partial DMA data in after a message */ 31952942Sralph {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAI), /* 18 */ 32052889Sbostic asc_resume_dma_in, ASC_CMD_XFER_INFO | ASC_CMD_DMA, 32152942Sralph &asc_scripts[SCRIPT_DATA_IN + 1]}, 32252889Sbostic 32352942Sralph /* resume data out after a message */ 32452942Sralph {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAO), /* 19 */ 32552942Sralph asc_resume_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA, 32652942Sralph &asc_scripts[SCRIPT_DATA_OUT + 1]}, 32752942Sralph 32852942Sralph /* resume partial DMA data out after a message */ 32952942Sralph {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_DATAO), /* 20 */ 33052889Sbostic asc_resume_dma_out, ASC_CMD_XFER_INFO | ASC_CMD_DMA, 33152942Sralph &asc_scripts[SCRIPT_DATA_OUT + 1]}, 33252942Sralph 33352942Sralph /* resume after a message when there is no more data */ 33452942Sralph {SCRIPT_MATCH(ASC_INT_BS, ASC_PHASE_STATUS), /* 21 */ 33552942Sralph script_nop, ASC_CMD_I_COMPLETE, 33652942Sralph &asc_scripts[SCRIPT_GET_STATUS]}, 33752889Sbostic }; 33852889Sbostic 33952889Sbostic /* 34052889Sbostic * State kept for each active SCSI device. 34152889Sbostic */ 34252889Sbostic typedef struct scsi_state { 34352889Sbostic script_t *script; /* saved script while processing error */ 34452889Sbostic int statusByte; /* status byte returned during STATUS_PHASE */ 34552889Sbostic int error; /* errno to pass back to device driver */ 34652889Sbostic u_char *dmaBufAddr; /* DMA buffer address */ 34752889Sbostic u_int dmaBufSize; /* DMA buffer size */ 34852889Sbostic int dmalen; /* amount to transfer in this chunk */ 34952889Sbostic int dmaresid; /* amount not transfered if chunk suspended */ 35052889Sbostic int buflen; /* total remaining amount of data to transfer */ 35152889Sbostic char *buf; /* current pointer within scsicmd->buf */ 35252889Sbostic int flags; /* see below */ 35352889Sbostic int msglen; /* number of message bytes to read */ 35452889Sbostic int msgcnt; /* number of message bytes received */ 35552889Sbostic u_char sync_period; /* DMA synchronous period */ 35652889Sbostic u_char sync_offset; /* DMA synchronous xfer offset or 0 if async */ 35752889Sbostic u_char msg_out; /* next MSG_OUT byte to send */ 35852889Sbostic u_char msg_in[16]; /* buffer for multibyte messages */ 35952889Sbostic } State; 36052889Sbostic 36152889Sbostic /* state flags */ 36252889Sbostic #define DISCONN 0x01 /* true if currently disconnected from bus */ 36353080Sralph #define DMA_IN_PROGRESS 0x02 /* true if data DMA started */ 36452889Sbostic #define DMA_IN 0x04 /* true if reading from SCSI device */ 36552889Sbostic #define DMA_OUT 0x10 /* true if writing to SCSI device */ 36652889Sbostic #define DID_SYNC 0x20 /* true if synchronous offset was negotiated */ 36752889Sbostic #define TRY_SYNC 0x40 /* true if try neg. synchronous offset */ 368*58658Sralph #define PARITY_ERR 0x80 /* true if parity error seen */ 36952889Sbostic 37052889Sbostic /* 37152889Sbostic * State kept for each active SCSI host interface (53C94). 37252889Sbostic */ 37352889Sbostic struct asc_softc { 37452889Sbostic asc_regmap_t *regs; /* chip address */ 37552889Sbostic volatile int *dmar; /* DMA address register address */ 37656819Sralph u_char *buff; /* RAM buffer address (uncached) */ 37752889Sbostic int myid; /* SCSI ID of this interface */ 37852889Sbostic int myidmask; /* ~(1 << myid) */ 37952889Sbostic int state; /* current SCSI connection state */ 38052889Sbostic int target; /* target SCSI ID if busy */ 38152889Sbostic script_t *script; /* next expected interrupt & action */ 38252889Sbostic ScsiCmd *cmd[ASC_NCMD]; /* active command indexed by SCSI ID */ 38352889Sbostic State st[ASC_NCMD]; /* state info for each active command */ 38456819Sralph void (*dma_start)(); /* Start dma routine */ 38556819Sralph void (*dma_end)(); /* End dma routine */ 38656819Sralph u_char *dma_next; 38756819Sralph int dma_xfer; /* Dma len still to go */ 38856819Sralph int min_period; /* Min transfer period clk/byte */ 38956819Sralph int max_period; /* Max transfer period clk/byte */ 39056819Sralph int ccf; /* CCF, whatever that really is? */ 39156819Sralph int timeout_250; /* 250ms timeout */ 39256819Sralph int tb_ticks; /* 4ns. ticks/tb channel ticks */ 39352889Sbostic } asc_softc[NASC]; 39452889Sbostic 39552889Sbostic #define ASC_STATE_IDLE 0 /* idle state */ 39652889Sbostic #define ASC_STATE_BUSY 1 /* selecting or currently connected */ 39752889Sbostic #define ASC_STATE_TARGET 2 /* currently selected as target */ 39852889Sbostic #define ASC_STATE_RESEL 3 /* currently waiting for reselect */ 39952889Sbostic 40052889Sbostic typedef struct asc_softc *asc_softc_t; 40152889Sbostic 40252889Sbostic /* 40356819Sralph * Dma operations. 40456819Sralph */ 40556819Sralph #define ASCDMA_READ 1 40656819Sralph #define ASCDMA_WRITE 2 40756819Sralph static void tb_dma_start(), tb_dma_end(), asic_dma_start(), asic_dma_end(); 40857233Sralph extern u_long asc_iomem; 40957233Sralph extern u_long asic_base; 41056819Sralph 41156819Sralph /* 41252889Sbostic * Definition of the controller for the auto-configuration program. 41352889Sbostic */ 41452889Sbostic int asc_probe(); 41552889Sbostic void asc_start(); 41652889Sbostic void asc_intr(); 41752889Sbostic struct driver ascdriver = { 41852889Sbostic "asc", asc_probe, asc_start, 0, asc_intr, 41952889Sbostic }; 42052889Sbostic 42152889Sbostic /* 42252889Sbostic * Test to see if device is present. 42352889Sbostic * Return true if found and initialized ok. 42452889Sbostic */ 42552889Sbostic asc_probe(cp) 42652889Sbostic register struct pmax_ctlr *cp; 42752889Sbostic { 42852889Sbostic register asc_softc_t asc; 42952889Sbostic register asc_regmap_t *regs; 43052889Sbostic int unit, id, s, i; 43157233Sralph int bufsiz; 43252889Sbostic 43352889Sbostic if ((unit = cp->pmax_unit) >= NASC) 43452889Sbostic return (0); 43552889Sbostic if (badaddr(cp->pmax_addr + ASC_OFFSET_53C94, 1)) 43652889Sbostic return (0); 43752889Sbostic asc = &asc_softc[unit]; 43852889Sbostic 43952889Sbostic /* 44052889Sbostic * Initialize hw descriptor, cache some pointers 44152889Sbostic */ 44252889Sbostic asc->regs = (asc_regmap_t *)(cp->pmax_addr + ASC_OFFSET_53C94); 44352889Sbostic 44456819Sralph /* 44556819Sralph * Set up machine dependencies. 44656819Sralph * 1) how to do dma 44756819Sralph * 2) timing based on turbochannel frequency 44856819Sralph */ 44956819Sralph switch (pmax_boardtype) { 45056819Sralph case DS_3MIN: 45156819Sralph case DS_MAXINE: 45257233Sralph case DS_3MAXPLUS: 45356819Sralph if (unit == 0) { 45457233Sralph asc->buff = (u_char *)MACH_PHYS_TO_UNCACHED(asc_iomem); 45557233Sralph bufsiz = 8192; 45657233Sralph *((volatile int *)ASIC_REG_SCSI_DMAPTR(asic_base)) = -1; 45757233Sralph *((volatile int *)ASIC_REG_SCSI_DMANPTR(asic_base)) = -1; 45857233Sralph *((volatile int *)ASIC_REG_SCSI_SCR(asic_base)) = 0; 45956819Sralph asc->dma_start = asic_dma_start; 46056819Sralph asc->dma_end = asic_dma_end; 46156819Sralph break; 46256819Sralph } 46356819Sralph /* 46456819Sralph * Fall through for turbochannel option. 46556819Sralph */ 46656819Sralph case DS_3MAX: 46756819Sralph default: 46856819Sralph asc->dmar = (volatile int *)(cp->pmax_addr + ASC_OFFSET_DMAR); 46956819Sralph asc->buff = (u_char *)(cp->pmax_addr + ASC_OFFSET_RAM); 47057233Sralph bufsiz = PER_TGT_DMA_SIZE; 47156819Sralph asc->dma_start = tb_dma_start; 47256819Sralph asc->dma_end = tb_dma_end; 47356819Sralph }; 47456819Sralph /* 47556819Sralph * Now for timing. The 3max has a 25Mhz tb whereas the 3min and 47656819Sralph * maxine are 12.5Mhz. 47756819Sralph */ 47856819Sralph switch (pmax_boardtype) { 47956819Sralph case DS_3MAX: 48057233Sralph case DS_3MAXPLUS: 48156819Sralph asc->min_period = ASC_MIN_PERIOD25; 48256819Sralph asc->max_period = ASC_MAX_PERIOD25; 48356819Sralph asc->ccf = ASC_CCF(25); 48456819Sralph asc->timeout_250 = ASC_TIMEOUT_250(25, asc->ccf); 48556819Sralph asc->tb_ticks = 10; 48656819Sralph break; 48756819Sralph case DS_3MIN: 48856819Sralph case DS_MAXINE: 48956819Sralph default: 49056819Sralph asc->min_period = ASC_MIN_PERIOD12; 49156819Sralph asc->max_period = ASC_MAX_PERIOD12; 49256819Sralph asc->ccf = ASC_CCF(13); 49356819Sralph asc->timeout_250 = ASC_TIMEOUT_250(13, asc->ccf); 49456819Sralph asc->tb_ticks = 20; 49556819Sralph break; 49656819Sralph }; 49756819Sralph 49852889Sbostic asc->state = ASC_STATE_IDLE; 49952889Sbostic asc->target = -1; 50052889Sbostic 50152889Sbostic regs = asc->regs; 50252889Sbostic 50352889Sbostic /* 50452889Sbostic * Reset chip, fully. Note that interrupts are already enabled. 50552889Sbostic */ 50652889Sbostic s = splbio(); 50752889Sbostic 50852889Sbostic /* preserve our ID for now */ 50952889Sbostic asc->myid = regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID; 51052889Sbostic asc->myidmask = ~(1 << asc->myid); 51152889Sbostic 51252889Sbostic asc_reset(asc, regs); 51352889Sbostic 51452889Sbostic /* 51552889Sbostic * Our SCSI id on the bus. 51652889Sbostic * The user can set this via the prom on 3maxen/pmaxen. 51752889Sbostic * If this changes it is easy to fix: make a default that 51852889Sbostic * can be changed as boot arg. 51952889Sbostic */ 52052889Sbostic #ifdef unneeded 52152889Sbostic regs->asc_cnfg1 = (regs->asc_cnfg1 & ~ASC_CNFG1_MY_BUS_ID) | 52252889Sbostic (scsi_initiator_id[unit] & 0x7); 52352889Sbostic #endif 52452889Sbostic id = regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID; 52552889Sbostic splx(s); 52652889Sbostic 52752889Sbostic /* 52852889Sbostic * Statically partition the DMA buffer between targets. 52952889Sbostic * This way we will eventually be able to attach/detach 53052889Sbostic * drives on-fly. And 18k/target is plenty for normal use. 53152889Sbostic */ 53252889Sbostic 53352889Sbostic /* 53452889Sbostic * Give each target its own DMA buffer region. 53552889Sbostic * We may want to try ping ponging buffers later. 53652889Sbostic */ 53752889Sbostic for (i = 0; i < ASC_NCMD; i++) { 53857233Sralph asc->st[i].dmaBufAddr = asc->buff + bufsiz * i; 53957233Sralph asc->st[i].dmaBufSize = bufsiz; 54052889Sbostic } 54152889Sbostic printf("asc%d at nexus0 csr 0x%x priority %d SCSI id %d\n", 54252889Sbostic unit, cp->pmax_addr, cp->pmax_pri, id); 54352889Sbostic return (1); 54452889Sbostic } 54552889Sbostic 54652889Sbostic /* 54752889Sbostic * Start activity on a SCSI device. 54852889Sbostic * We maintain information on each device separately since devices can 54952889Sbostic * connect/disconnect during an operation. 55052889Sbostic */ 55152889Sbostic void 55252889Sbostic asc_start(scsicmd) 55352889Sbostic register ScsiCmd *scsicmd; /* command to start */ 55452889Sbostic { 55552889Sbostic register struct scsi_device *sdp = scsicmd->sd; 55652889Sbostic register asc_softc_t asc = &asc_softc[sdp->sd_ctlr]; 55752889Sbostic int s; 55852889Sbostic 55952889Sbostic s = splbio(); 56052889Sbostic /* 56152889Sbostic * Check if another command is already in progress. 56252889Sbostic * We may have to change this if we allow SCSI devices with 56352889Sbostic * separate LUNs. 56452889Sbostic */ 56552889Sbostic if (asc->cmd[sdp->sd_drive]) { 56652889Sbostic printf("asc%d: device %s busy at start\n", sdp->sd_ctlr, 56752889Sbostic sdp->sd_driver->d_name); 56852889Sbostic (*sdp->sd_driver->d_done)(scsicmd->unit, EBUSY, 56952889Sbostic scsicmd->buflen, 0); 57052889Sbostic splx(s); 57152889Sbostic } 57252889Sbostic asc->cmd[sdp->sd_drive] = scsicmd; 57352889Sbostic asc_startcmd(asc, sdp->sd_drive); 57452889Sbostic splx(s); 57552889Sbostic } 57652889Sbostic 57752889Sbostic static void 57852889Sbostic asc_reset(asc, regs) 57952889Sbostic asc_softc_t asc; 58052889Sbostic asc_regmap_t *regs; 58152889Sbostic { 58252889Sbostic 58352889Sbostic /* 58452889Sbostic * Reset chip and wait till done 58552889Sbostic */ 58652889Sbostic regs->asc_cmd = ASC_CMD_RESET; 58752889Sbostic MachEmptyWriteBuffer(); DELAY(25); 58852889Sbostic 58952889Sbostic /* spec says this is needed after reset */ 59052889Sbostic regs->asc_cmd = ASC_CMD_NOP; 59152889Sbostic MachEmptyWriteBuffer(); DELAY(25); 59252889Sbostic 59352889Sbostic /* 59452889Sbostic * Set up various chip parameters 59552889Sbostic */ 59656819Sralph regs->asc_ccf = asc->ccf; 59752889Sbostic MachEmptyWriteBuffer(); DELAY(25); 59856819Sralph regs->asc_sel_timo = asc->timeout_250; 59952889Sbostic /* restore our ID */ 60052889Sbostic regs->asc_cnfg1 = asc->myid | ASC_CNFG1_P_CHECK; 60157233Sralph /* include ASC_CNFG2_SCSI2 if you want to allow SCSI II commands */ 60257233Sralph regs->asc_cnfg2 = /* ASC_CNFG2_RFB | ASC_CNFG2_SCSI2 | */ ASC_CNFG2_EPL; 60352889Sbostic regs->asc_cnfg3 = 0; 60452889Sbostic /* zero anything else */ 60552889Sbostic ASC_TC_PUT(regs, 0); 60656819Sralph regs->asc_syn_p = asc->min_period; 60752889Sbostic regs->asc_syn_o = 0; /* async for now */ 60852889Sbostic MachEmptyWriteBuffer(); 60952889Sbostic } 61052889Sbostic 61152889Sbostic /* 61252889Sbostic * Start a SCSI command on a target. 61352889Sbostic */ 61452889Sbostic static void 61552889Sbostic asc_startcmd(asc, target) 61652889Sbostic asc_softc_t asc; 61752889Sbostic int target; 61852889Sbostic { 61952889Sbostic register asc_regmap_t *regs; 62052889Sbostic register ScsiCmd *scsicmd; 62152889Sbostic register State *state; 62252889Sbostic int len; 62352889Sbostic 62452889Sbostic /* 62552889Sbostic * See if another target is currently selected on this SCSI bus. 62652889Sbostic */ 62752889Sbostic if (asc->target >= 0) 62852889Sbostic return; 62952889Sbostic 63052889Sbostic regs = asc->regs; 63152889Sbostic 63252889Sbostic /* 633*58658Sralph * If a reselection is in progress, it is Ok to ignore it since 634*58658Sralph * the ASC will automatically cancel the command and flush 635*58658Sralph * the FIFO if the ASC is reselected before the command starts. 636*58658Sralph * If we try to use ASC_CMD_DISABLE_SEL, we can hang the system if 637*58658Sralph * a reselect occurs before starting the command. 63852889Sbostic */ 63952889Sbostic 64052889Sbostic asc->state = ASC_STATE_BUSY; 64152889Sbostic asc->target = target; 64252889Sbostic 64352889Sbostic /* cache some pointers */ 64452889Sbostic scsicmd = asc->cmd[target]; 64552889Sbostic state = &asc->st[target]; 64652889Sbostic 64752889Sbostic #ifdef DEBUG 64852889Sbostic if (asc_debug > 1) { 64952889Sbostic printf("asc_startcmd: %s target %d cmd %x len %d\n", 65052889Sbostic scsicmd->sd->sd_driver->d_name, target, 65152889Sbostic scsicmd->cmd[0], scsicmd->buflen); 65252889Sbostic } 65352889Sbostic asc_debug_cmd = scsicmd->cmd[0]; 65452889Sbostic if (scsicmd->cmd[0] == SCSI_READ_EXT) { 65552889Sbostic asc_debug_bn = (scsicmd->cmd[2] << 24) | 65652889Sbostic (scsicmd->cmd[3] << 16) | 65752889Sbostic (scsicmd->cmd[4] << 8) | 65852889Sbostic scsicmd->cmd[5]; 65952889Sbostic asc_debug_sz = (scsicmd->cmd[7] << 8) | scsicmd->cmd[8]; 66052889Sbostic } 66153080Sralph asc_logp->status = PACK(asc - asc_softc, 0, 0, asc_debug_cmd); 66252889Sbostic asc_logp->target = asc->target; 66352889Sbostic asc_logp->state = 0; 66453080Sralph asc_logp->msg = 0xff; 665*58658Sralph asc_logp->resid = scsicmd->buflen; 66652889Sbostic if (++asc_logp >= &asc_log[NLOG]) 66752889Sbostic asc_logp = asc_log; 66852889Sbostic #endif 66952889Sbostic 67052889Sbostic /* 67152889Sbostic * Init the chip and target state. 67252889Sbostic */ 67353080Sralph state->flags = state->flags & DID_SYNC; 67452889Sbostic state->error = 0; 67552889Sbostic state->script = (script_t *)0; 67652889Sbostic state->msg_out = SCSI_NO_OP; 67752889Sbostic 67852889Sbostic /* 67952889Sbostic * Copy command data to the DMA buffer. 68052889Sbostic */ 68152889Sbostic len = scsicmd->cmdlen; 68252889Sbostic state->dmalen = len; 68352889Sbostic bcopy(scsicmd->cmd, state->dmaBufAddr, len); 68452889Sbostic 68552889Sbostic /* check for simple SCSI command with no data transfer */ 68652889Sbostic if ((state->buflen = scsicmd->buflen) == 0) { 68752889Sbostic /* check for sync negotiation */ 68852889Sbostic if ((scsicmd->flags & SCSICMD_USE_SYNC) && 68952889Sbostic !(state->flags & DID_SYNC)) { 69052889Sbostic asc->script = &asc_scripts[SCRIPT_TRY_SYNC]; 69152889Sbostic state->flags |= TRY_SYNC; 69252889Sbostic } else 69352889Sbostic asc->script = &asc_scripts[SCRIPT_SIMPLE]; 69452889Sbostic state->buf = (char *)0; 69552889Sbostic } else if (scsicmd->flags & SCSICMD_DATA_TO_DEVICE) { 69652889Sbostic asc->script = &asc_scripts[SCRIPT_DATA_OUT]; 69753080Sralph state->buf = scsicmd->buf; 69852889Sbostic state->flags |= DMA_OUT; 69952889Sbostic } else { 70052889Sbostic asc->script = &asc_scripts[SCRIPT_DATA_IN]; 70152889Sbostic state->buf = scsicmd->buf; 70252889Sbostic state->flags |= DMA_IN; 70352889Sbostic } 70452889Sbostic 70552889Sbostic /* preload the FIFO with the message to be sent */ 70652942Sralph regs->asc_fifo = SCSI_DIS_REC_IDENTIFY; 70756819Sralph MachEmptyWriteBuffer(); 70852889Sbostic 70952889Sbostic /* start the asc */ 71056819Sralph (*asc->dma_start)(asc, state, state->dmaBufAddr, ASCDMA_WRITE); 71152889Sbostic ASC_TC_PUT(regs, len); 71256819Sralph readback(regs->asc_cmd); 71352889Sbostic 71452889Sbostic regs->asc_dbus_id = target; 71556819Sralph readback(regs->asc_dbus_id); 71652889Sbostic regs->asc_syn_p = state->sync_period; 71756819Sralph readback(regs->asc_syn_p); 71852889Sbostic regs->asc_syn_o = state->sync_offset; 71956819Sralph readback(regs->asc_syn_o); 72052889Sbostic 72152889Sbostic if (state->flags & TRY_SYNC) 72253080Sralph regs->asc_cmd = ASC_CMD_SEL_ATN_STOP; 72352889Sbostic else 72452889Sbostic regs->asc_cmd = ASC_CMD_SEL_ATN | ASC_CMD_DMA; 72556819Sralph readback(regs->asc_cmd); 72652889Sbostic } 72752889Sbostic 72852889Sbostic /* 72952889Sbostic * Interrupt routine 73052889Sbostic * Take interrupts from the chip 73152889Sbostic * 73252889Sbostic * Implementation: 73352889Sbostic * Move along the current command's script if 73452889Sbostic * all is well, invoke error handler if not. 73552889Sbostic */ 73652889Sbostic void 73752889Sbostic asc_intr(unit) 73852889Sbostic int unit; 73952889Sbostic { 74052889Sbostic register asc_softc_t asc = &asc_softc[unit]; 74152889Sbostic register asc_regmap_t *regs = asc->regs; 74252889Sbostic register State *state; 74352889Sbostic register script_t *scpt; 74452889Sbostic register int ss, ir, status; 74552889Sbostic 74652889Sbostic /* collect ephemeral information */ 74752889Sbostic status = regs->asc_status; 74853080Sralph again: 74952889Sbostic ss = regs->asc_ss; 75052889Sbostic ir = regs->asc_intr; /* this resets the previous two */ 75152889Sbostic scpt = asc->script; 75252889Sbostic 75352889Sbostic #ifdef DEBUG 75452889Sbostic asc_logp->status = PACK(unit, status, ss, ir); 75552889Sbostic asc_logp->target = (asc->state == ASC_STATE_BUSY) ? asc->target : -1; 75652889Sbostic asc_logp->state = scpt - asc_scripts; 75752889Sbostic asc_logp->msg = -1; 758*58658Sralph asc_logp->resid = 0; 75952889Sbostic if (++asc_logp >= &asc_log[NLOG]) 76052889Sbostic asc_logp = asc_log; 76152889Sbostic if (asc_debug > 2) 76252889Sbostic printf("asc_intr: status %x ss %x ir %x cond %d:%x\n", 76352889Sbostic status, ss, ir, scpt - asc_scripts, scpt->condition); 76452889Sbostic #endif 76552889Sbostic 76652889Sbostic /* check the expected state */ 76752889Sbostic if (SCRIPT_MATCH(ir, status) == scpt->condition) { 76852889Sbostic /* 76952889Sbostic * Perform the appropriate operation, then proceed. 77052889Sbostic */ 77152889Sbostic if ((*scpt->action)(asc, status, ss, ir)) { 77252889Sbostic regs->asc_cmd = scpt->command; 77356819Sralph readback(regs->asc_cmd); 77452889Sbostic asc->script = scpt->next; 77552889Sbostic } 77652889Sbostic goto done; 77752889Sbostic } 77852889Sbostic 779*58658Sralph /* 780*58658Sralph * Check for parity error. 781*58658Sralph * Hardware will automatically set ATN 782*58658Sralph * to request the device for a MSG_OUT phase. 783*58658Sralph */ 784*58658Sralph if (status & ASC_CSR_PE) { 785*58658Sralph printf("asc%d: SCSI device %d: incomming parity error seen\n", 786*58658Sralph asc - asc_softc, asc->target); 787*58658Sralph asc->st[asc->target].flags |= PARITY_ERR; 788*58658Sralph } 789*58658Sralph 790*58658Sralph /* 791*58658Sralph * Check for gross error. 792*58658Sralph * Probably a bug in a device driver. 793*58658Sralph */ 794*58658Sralph if (status & ASC_CSR_GE) { 795*58658Sralph printf("asc%d: SCSI device %d: gross error\n", 796*58658Sralph asc - asc_softc, asc->target); 797*58658Sralph goto abort; 798*58658Sralph } 799*58658Sralph 80052889Sbostic /* check for message in or out */ 80152889Sbostic if ((ir & ~ASC_INT_FC) == ASC_INT_BS) { 80252889Sbostic register int len, fifo; 80352889Sbostic 80452889Sbostic state = &asc->st[asc->target]; 80552889Sbostic switch (ASC_PHASE(status)) { 80653080Sralph case ASC_PHASE_DATAI: 80753080Sralph case ASC_PHASE_DATAO: 80853080Sralph ASC_TC_GET(regs, len); 80953080Sralph fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; 81053080Sralph printf("asc_intr: data overrun: buflen %d dmalen %d tc %d fifo %d\n", 81153080Sralph state->buflen, state->dmalen, len, fifo); 81253080Sralph goto abort; 81353080Sralph 81452889Sbostic case ASC_PHASE_MSG_IN: 81552889Sbostic break; 81652889Sbostic 81752889Sbostic case ASC_PHASE_MSG_OUT: 818*58658Sralph /* 819*58658Sralph * Check for parity error. 820*58658Sralph * Hardware will automatically set ATN 821*58658Sralph * to request the device for a MSG_OUT phase. 822*58658Sralph */ 823*58658Sralph if (state->flags & PARITY_ERR) { 824*58658Sralph state->flags &= ~PARITY_ERR; 825*58658Sralph state->msg_out = SCSI_MESSAGE_PARITY_ERROR; 826*58658Sralph /* reset message in counter */ 827*58658Sralph state->msglen = 0; 828*58658Sralph } else 829*58658Sralph state->msg_out = SCSI_NO_OP; 83052889Sbostic regs->asc_fifo = state->msg_out; 83152889Sbostic regs->asc_cmd = ASC_CMD_XFER_INFO; 83256819Sralph readback(regs->asc_cmd); 83352889Sbostic goto done; 83452889Sbostic 83552889Sbostic case ASC_PHASE_STATUS: 83652889Sbostic /* probably an error in the SCSI command */ 83752889Sbostic asc->script = &asc_scripts[SCRIPT_GET_STATUS]; 83852889Sbostic regs->asc_cmd = ASC_CMD_I_COMPLETE; 83956819Sralph readback(regs->asc_cmd); 84052889Sbostic goto done; 84152889Sbostic 84252889Sbostic default: 84352889Sbostic goto abort; 84452889Sbostic } 84552889Sbostic 84652889Sbostic if (state->script) 84752889Sbostic goto abort; 84852889Sbostic 84952889Sbostic /* check for DMA in progress */ 85052889Sbostic ASC_TC_GET(regs, len); 85152889Sbostic fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; 85252889Sbostic /* flush any data in the FIFO */ 85352889Sbostic if (fifo) { 85453080Sralph if (state->flags & DMA_OUT) 85553080Sralph len += fifo; 85653080Sralph else if (state->flags & DMA_IN) { 85753080Sralph u_char *cp; 85853080Sralph 85953080Sralph printf("asc_intr: IN: dmalen %d len %d fifo %d\n", 86053080Sralph state->dmalen, len, fifo); /* XXX */ 86153080Sralph len += fifo; 86253080Sralph cp = state->dmaBufAddr + (state->dmalen - len); 86353080Sralph while (fifo-- > 0) 86453080Sralph *cp++ = regs->asc_fifo; 86553080Sralph } else 86653080Sralph printf("asc_intr: dmalen %d len %d fifo %d\n", 86753080Sralph state->dmalen, len, fifo); /* XXX */ 86852889Sbostic regs->asc_cmd = ASC_CMD_FLUSH; 86956819Sralph readback(regs->asc_cmd); 87056819Sralph DELAY(2); 87152889Sbostic } 87252889Sbostic if (len) { 87352889Sbostic /* save number of bytes still to be sent or received */ 87452889Sbostic state->dmaresid = len; 87552889Sbostic /* setup state to resume to */ 87652889Sbostic if (state->flags & DMA_IN) 87752889Sbostic state->script = 87852889Sbostic &asc_scripts[SCRIPT_RESUME_DMA_IN]; 87952889Sbostic else if (state->flags & DMA_OUT) 88052889Sbostic state->script = 88152889Sbostic &asc_scripts[SCRIPT_RESUME_DMA_OUT]; 88252889Sbostic else 88352889Sbostic state->script = asc->script; 884*58658Sralph #ifdef DEBUG 885*58658Sralph if (asc_logp == asc_log) 886*58658Sralph asc_log[NLOG - 1].resid = len; 887*58658Sralph else 888*58658Sralph asc_logp[-1].resid = len; 889*58658Sralph #endif 89052889Sbostic } else { 89152889Sbostic /* setup state to resume to */ 89252942Sralph if (state->flags & DMA_IN) { 89353080Sralph if (state->flags & DMA_IN_PROGRESS) { 89453080Sralph state->flags &= ~DMA_IN_PROGRESS; 89556819Sralph (*asc->dma_end)(asc, state, ASCDMA_READ); 89652942Sralph len = state->dmalen; 89752942Sralph bcopy(state->dmaBufAddr, state->buf, 89852942Sralph len); 89952942Sralph state->buf += len; 90052942Sralph state->buflen -= len; 90153080Sralph } 90252942Sralph if (state->buflen) 90352942Sralph state->script = 90452942Sralph &asc_scripts[SCRIPT_RESUME_IN]; 90552942Sralph else 90652942Sralph state->script = 90752942Sralph &asc_scripts[SCRIPT_RESUME_NO_DATA]; 90852942Sralph } else if (state->flags & DMA_OUT) { 90952942Sralph /* 91052942Sralph * If this is the last chunk, the next expected 91152942Sralph * state is to get status. 91252942Sralph */ 91353080Sralph if (state->flags & DMA_IN_PROGRESS) { 91453080Sralph state->flags &= ~DMA_IN_PROGRESS; 91556819Sralph (*asc->dma_end)(asc, state, ASCDMA_WRITE); 91653080Sralph len = state->dmalen; 91753080Sralph state->buf += len; 91853080Sralph state->buflen -= len; 91953080Sralph } 92052942Sralph if (state->buflen) 92152942Sralph state->script = 92252942Sralph &asc_scripts[SCRIPT_RESUME_OUT]; 92352942Sralph else 92452942Sralph state->script = 92552942Sralph &asc_scripts[SCRIPT_RESUME_NO_DATA]; 92653080Sralph } else if (asc->script == &asc_scripts[SCRIPT_SIMPLE]) 92753080Sralph state->script = 928*58658Sralph &asc_scripts[SCRIPT_RESUME_NO_DATA]; 92953080Sralph else 93052889Sbostic state->script = asc->script; 93152889Sbostic } 93252889Sbostic 93352889Sbostic /* setup to receive a message */ 93452889Sbostic asc->script = &asc_scripts[SCRIPT_MSG_IN]; 93552889Sbostic state->msglen = 0; 93652889Sbostic regs->asc_cmd = ASC_CMD_XFER_INFO; 93756819Sralph readback(regs->asc_cmd); 93852889Sbostic goto done; 93952889Sbostic } 94052889Sbostic 94152889Sbostic /* check for SCSI bus reset */ 94252889Sbostic if (ir & ASC_INT_RESET) { 94352889Sbostic register int i; 94452889Sbostic 94552889Sbostic printf("asc%d: SCSI bus reset!!\n", asc - asc_softc); 94652889Sbostic /* need to flush any pending commands */ 94752889Sbostic for (i = 0; i < ASC_NCMD; i++) { 94852889Sbostic if (!asc->cmd[i]) 94952889Sbostic continue; 95052889Sbostic asc->st[i].error = EIO; 95152889Sbostic asc_end(asc, 0, 0, 0); 95252889Sbostic } 95352889Sbostic /* rearbitrate synchronous offset */ 95452889Sbostic for (i = 0; i < ASC_NCMD; i++) { 95552889Sbostic asc->st[i].sync_offset = 0; 95652889Sbostic asc->st[i].flags = 0; 95752889Sbostic } 95852889Sbostic asc->target = -1; 95952889Sbostic return; 96052889Sbostic } 96152889Sbostic 96252889Sbostic /* check for command errors */ 96352889Sbostic if (ir & ASC_INT_ILL) 96452889Sbostic goto abort; 96552889Sbostic 96652889Sbostic /* check for disconnect */ 96752889Sbostic if (ir & ASC_INT_DISC) { 96852889Sbostic state = &asc->st[asc->target]; 96952889Sbostic switch (ASC_SS(ss)) { 97052889Sbostic case 0: /* device did not respond */ 971*58658Sralph /* check for one of the starting scripts */ 972*58658Sralph switch (asc->script - asc_scripts) { 973*58658Sralph case SCRIPT_TRY_SYNC: 974*58658Sralph case SCRIPT_SIMPLE: 975*58658Sralph case SCRIPT_DATA_IN: 976*58658Sralph case SCRIPT_DATA_OUT: 977*58658Sralph if (regs->asc_flags & ASC_FLAGS_FIFO_CNT) { 978*58658Sralph regs->asc_cmd = ASC_CMD_FLUSH; 979*58658Sralph readback(regs->asc_cmd); 980*58658Sralph } 981*58658Sralph state->error = ENXIO; 982*58658Sralph asc_end(asc, status, ss, ir); 983*58658Sralph return; 984*58658Sralph } 985*58658Sralph /* FALLTHROUGH */ 98652889Sbostic 98752889Sbostic default: 988*58658Sralph printf("asc%d: SCSI device %d: unexpected disconnect\n", 989*58658Sralph asc - asc_softc, asc->target); 99057233Sralph /* 99157233Sralph * On rare occasions my RZ24 does a disconnect during 99257233Sralph * data in phase and the following seems to keep it 99357233Sralph * happy. 99457233Sralph * XXX Should a scsi disk ever do this?? 99557233Sralph */ 99657233Sralph asc->script = &asc_scripts[SCRIPT_RESEL]; 99757233Sralph asc->state = ASC_STATE_RESEL; 99857233Sralph state->flags |= DISCONN; 99957233Sralph regs->asc_cmd = ASC_CMD_ENABLE_SEL; 100057233Sralph readback(regs->asc_cmd); 100157233Sralph return; 100252889Sbostic } 100352889Sbostic } 100452889Sbostic 100552889Sbostic /* check for reselect */ 100652889Sbostic if (ir & ASC_INT_RESEL) { 100752889Sbostic unsigned fifo, id, msg; 100852889Sbostic 100952889Sbostic fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; 101052889Sbostic if (fifo < 2) 101152889Sbostic goto abort; 101252889Sbostic /* read unencoded SCSI ID and convert to binary */ 101352889Sbostic msg = regs->asc_fifo & asc->myidmask; 101452889Sbostic for (id = 0; (msg & 1) == 0; id++) 101552889Sbostic msg >>= 1; 101652889Sbostic /* read identify message */ 101752889Sbostic msg = regs->asc_fifo; 101852889Sbostic #ifdef DEBUG 101952889Sbostic if (asc_logp == asc_log) 102052889Sbostic asc_log[NLOG - 1].msg = msg; 102152889Sbostic else 102252889Sbostic asc_logp[-1].msg = msg; 102352889Sbostic #endif 102452889Sbostic asc->state = ASC_STATE_BUSY; 102552889Sbostic asc->target = id; 102652889Sbostic state = &asc->st[id]; 102752889Sbostic asc->script = state->script; 102852889Sbostic state->script = (script_t *)0; 102952889Sbostic if (!(state->flags & DISCONN)) 103052889Sbostic goto abort; 103152889Sbostic state->flags &= ~DISCONN; 103253080Sralph regs->asc_syn_p = state->sync_period; 103353080Sralph regs->asc_syn_o = state->sync_offset; 103452889Sbostic regs->asc_cmd = ASC_CMD_MSG_ACPT; 103556819Sralph readback(regs->asc_cmd); 103652889Sbostic goto done; 103752889Sbostic } 103852889Sbostic 103952889Sbostic /* check if we are being selected as a target */ 104052889Sbostic if (ir & (ASC_INT_SEL | ASC_INT_SEL_ATN)) 104152889Sbostic goto abort; 104252889Sbostic 1043*58658Sralph /* 1044*58658Sralph * 'ir' must be just ASC_INT_FC. 1045*58658Sralph * This is normal if canceling an ASC_ENABLE_SEL. 1046*58658Sralph */ 1047*58658Sralph 104852889Sbostic done: 104952889Sbostic MachEmptyWriteBuffer(); 105053080Sralph /* watch out for HW race conditions and setup & hold time violations */ 105153080Sralph ir = regs->asc_status; 105253080Sralph while (ir != (status = regs->asc_status)) 105353080Sralph ir = status; 105453080Sralph if (status & ASC_CSR_INT) 105552889Sbostic goto again; 105652889Sbostic return; 105752889Sbostic 105852889Sbostic abort: 105952889Sbostic #ifdef DEBUG 106052889Sbostic asc_DumpLog("asc_intr"); 106152889Sbostic #endif 106252889Sbostic #if 0 106352889Sbostic panic("asc_intr"); 106452889Sbostic #else 106552889Sbostic for (;;); 106652889Sbostic #endif 106752889Sbostic } 106852889Sbostic 106952889Sbostic /* 107052889Sbostic * All the many little things that the interrupt 107152889Sbostic * routine might switch to. 107252889Sbostic */ 107352889Sbostic 107452889Sbostic /* ARGSUSED */ 107552889Sbostic static int 107652889Sbostic script_nop(asc, status, ss, ir) 107752889Sbostic register asc_softc_t asc; 107852889Sbostic register int status, ss, ir; 107952889Sbostic { 108052889Sbostic return (1); 108152889Sbostic } 108252889Sbostic 108352889Sbostic /* ARGSUSED */ 108452889Sbostic static int 108552889Sbostic asc_get_status(asc, status, ss, ir) 108652889Sbostic register asc_softc_t asc; 108752889Sbostic register int status, ss, ir; 108852889Sbostic { 108952889Sbostic register asc_regmap_t *regs = asc->regs; 109052889Sbostic register int data; 109152889Sbostic 109252889Sbostic /* 109352889Sbostic * Get the last two bytes in the FIFO. 109452889Sbostic */ 109552889Sbostic if ((data = regs->asc_flags & ASC_FLAGS_FIFO_CNT) != 2) { 109652889Sbostic printf("asc_get_status: fifo cnt %d\n", data); /* XXX */ 1097*58658Sralph asc_DumpLog("get_status"); /* XXX */ 109852889Sbostic if (data < 2) { 109952889Sbostic asc->regs->asc_cmd = ASC_CMD_MSG_ACPT; 110056819Sralph readback(asc->regs->asc_cmd); 110152889Sbostic return (0); 110252889Sbostic } 110352889Sbostic do { 110452889Sbostic data = regs->asc_fifo; 110552889Sbostic } while ((regs->asc_flags & ASC_FLAGS_FIFO_CNT) > 2); 110652889Sbostic } 110752889Sbostic 110852889Sbostic /* save the status byte */ 110952889Sbostic asc->st[asc->target].statusByte = data = regs->asc_fifo; 111052889Sbostic #ifdef DEBUG 111152889Sbostic if (asc_logp == asc_log) 111252889Sbostic asc_log[NLOG - 1].msg = data; 111352889Sbostic else 111452889Sbostic asc_logp[-1].msg = data; 111552889Sbostic #endif 111652889Sbostic 111752889Sbostic /* get the (presumed) command_complete message */ 111852889Sbostic if ((data = regs->asc_fifo) == SCSI_COMMAND_COMPLETE) 111952889Sbostic return (1); 112052889Sbostic 112152889Sbostic #ifdef DEBUG 112252889Sbostic printf("asc_get_status: status %x cmd %x\n", 112352889Sbostic asc->st[asc->target].statusByte, data); 112452889Sbostic asc_DumpLog("asc_get_status"); 112552889Sbostic #endif 112652889Sbostic return (0); 112752889Sbostic } 112852889Sbostic 112952889Sbostic /* ARGSUSED */ 113052889Sbostic static int 113152889Sbostic asc_end(asc, status, ss, ir) 113252889Sbostic register asc_softc_t asc; 113352889Sbostic register int status, ss, ir; 113452889Sbostic { 113552889Sbostic register ScsiCmd *scsicmd; 113652889Sbostic register State *state; 113752889Sbostic register int i, target; 113852889Sbostic 113952889Sbostic asc->state = ASC_STATE_IDLE; 114052889Sbostic target = asc->target; 114152889Sbostic asc->target = -1; 114252889Sbostic scsicmd = asc->cmd[target]; 114352889Sbostic asc->cmd[target] = (ScsiCmd *)0; 114452889Sbostic state = &asc->st[target]; 114552889Sbostic 114652889Sbostic #ifdef DEBUG 114752889Sbostic if (asc_debug > 1) { 114852889Sbostic printf("asc_end: %s target %d cmd %x err %d resid %d\n", 114952889Sbostic scsicmd->sd->sd_driver->d_name, target, 115052889Sbostic scsicmd->cmd[0], state->error, state->buflen); 115152889Sbostic } 115252889Sbostic #endif 115352889Sbostic #ifdef DIAGNOSTIC 115452889Sbostic if (target < 0 || !scsicmd) 115552889Sbostic panic("asc_end"); 115652889Sbostic #endif 115752889Sbostic 115852889Sbostic /* look for disconnected devices */ 115952889Sbostic for (i = 0; i < ASC_NCMD; i++) { 116052889Sbostic if (!asc->cmd[i] || !(asc->st[i].flags & DISCONN)) 116152889Sbostic continue; 116252889Sbostic asc->regs->asc_cmd = ASC_CMD_ENABLE_SEL; 116356819Sralph readback(asc->regs->asc_cmd); 116452889Sbostic asc->state = ASC_STATE_RESEL; 116552889Sbostic asc->script = &asc_scripts[SCRIPT_RESEL]; 116652889Sbostic break; 116752889Sbostic } 116852889Sbostic 1169*58658Sralph /* 1170*58658Sralph * Look for another device that is ready. 1171*58658Sralph * May want to keep last one started and increment for fairness 1172*58658Sralph * rather than always starting at zero. 1173*58658Sralph */ 117452889Sbostic for (i = 0; i < ASC_NCMD; i++) { 117552889Sbostic /* don't restart a disconnected command */ 117652889Sbostic if (!asc->cmd[i] || (asc->st[i].flags & DISCONN)) 117752889Sbostic continue; 117852889Sbostic asc_startcmd(asc, i); 117952889Sbostic break; 118052889Sbostic } 118152889Sbostic 118252889Sbostic /* signal device driver that the command is done */ 118352889Sbostic (*scsicmd->sd->sd_driver->d_done)(scsicmd->unit, state->error, 118452889Sbostic state->buflen, state->statusByte); 118552889Sbostic 118652889Sbostic return (0); 118752889Sbostic } 118852889Sbostic 118952889Sbostic /* ARGSUSED */ 119052889Sbostic static int 119152889Sbostic asc_dma_in(asc, status, ss, ir) 119252889Sbostic register asc_softc_t asc; 119352889Sbostic register int status, ss, ir; 119452889Sbostic { 119552889Sbostic register asc_regmap_t *regs = asc->regs; 119652889Sbostic register State *state = &asc->st[asc->target]; 119753080Sralph register int len; 119852889Sbostic 119952889Sbostic /* check for previous chunk in buffer */ 120053080Sralph if (state->flags & DMA_IN_PROGRESS) { 120152889Sbostic /* 120252889Sbostic * Only count bytes that have been copied to memory. 120352889Sbostic * There may be some bytes in the FIFO if synchonous transfers 120452889Sbostic * are in progress. 120552889Sbostic */ 120656819Sralph (*asc->dma_end)(asc, state, ASCDMA_READ); 120752889Sbostic ASC_TC_GET(regs, len); 120852889Sbostic len = state->dmalen - len; 120952889Sbostic bcopy(state->dmaBufAddr, state->buf, len); 121052889Sbostic state->buf += len; 121152889Sbostic state->buflen -= len; 121253080Sralph } 121352889Sbostic 121452942Sralph /* setup to start reading the next chunk */ 121552889Sbostic len = state->buflen; 121652889Sbostic if (len > state->dmaBufSize) 121752889Sbostic len = state->dmaBufSize; 121852889Sbostic state->dmalen = len; 121956819Sralph (*asc->dma_start)(asc, state, state->dmaBufAddr, ASCDMA_READ); 122052889Sbostic ASC_TC_PUT(regs, len); 122152942Sralph #ifdef DEBUG 122252942Sralph if (asc_debug > 2) 122352942Sralph printf("asc_dma_in: buflen %d, len %d\n", state->buflen, len); 122452942Sralph #endif 122552942Sralph 122652942Sralph /* check for next chunk */ 122753080Sralph state->flags |= DMA_IN_PROGRESS; 122852889Sbostic if (len != state->buflen) { 122952889Sbostic regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA; 123056819Sralph readback(regs->asc_cmd); 123152942Sralph asc->script = &asc_scripts[SCRIPT_CONTINUE_IN]; 123252889Sbostic return (0); 123352889Sbostic } 123452889Sbostic return (1); 123552889Sbostic } 123652889Sbostic 123752889Sbostic /* ARGSUSED */ 123852889Sbostic static int 123952889Sbostic asc_last_dma_in(asc, status, ss, ir) 124052889Sbostic register asc_softc_t asc; 124152889Sbostic register int status, ss, ir; 124252889Sbostic { 124352889Sbostic register asc_regmap_t *regs = asc->regs; 124452889Sbostic register State *state = &asc->st[asc->target]; 124552889Sbostic register int len, fifo; 124652889Sbostic 124752889Sbostic /* copy data from buffer to main memory */ 124856819Sralph (*asc->dma_end)(asc, state, ASCDMA_READ); 124952889Sbostic ASC_TC_GET(regs, len); 125052889Sbostic fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; 125152889Sbostic #ifdef DEBUG 125252942Sralph if (asc_debug > 2) 125352889Sbostic printf("asc_last_dma_in: buflen %d dmalen %d tc %d fifo %d\n", 125452889Sbostic state->buflen, state->dmalen, len, fifo); 125552889Sbostic #endif 125652889Sbostic if (fifo) { 125752942Sralph /* device must be trying to send more than we expect */ 125852889Sbostic regs->asc_cmd = ASC_CMD_FLUSH; 125956819Sralph readback(regs->asc_cmd); 126052889Sbostic } 126153080Sralph state->flags &= ~DMA_IN_PROGRESS; 126252889Sbostic len = state->dmalen - len; 126352889Sbostic state->buflen -= len; 126452889Sbostic bcopy(state->dmaBufAddr, state->buf, len); 126552889Sbostic 126652889Sbostic return (1); 126752889Sbostic } 126852889Sbostic 126952889Sbostic /* ARGSUSED */ 127052889Sbostic static int 127152942Sralph asc_resume_in(asc, status, ss, ir) 127252942Sralph register asc_softc_t asc; 127352942Sralph register int status, ss, ir; 127452942Sralph { 127552942Sralph register asc_regmap_t *regs = asc->regs; 127652942Sralph register State *state = &asc->st[asc->target]; 127752942Sralph register int len; 127852942Sralph 127952942Sralph /* setup to start reading the next chunk */ 128052942Sralph len = state->buflen; 128152942Sralph if (len > state->dmaBufSize) 128252942Sralph len = state->dmaBufSize; 128352942Sralph state->dmalen = len; 128456819Sralph (*asc->dma_start)(asc, state, state->dmaBufAddr, ASCDMA_READ); 128552942Sralph ASC_TC_PUT(regs, len); 128652942Sralph #ifdef DEBUG 128752942Sralph if (asc_debug > 2) 128852942Sralph printf("asc_resume_in: buflen %d, len %d\n", state->buflen, 128952942Sralph len); 129052942Sralph #endif 129152942Sralph 129252942Sralph /* check for next chunk */ 129353080Sralph state->flags |= DMA_IN_PROGRESS; 129452942Sralph if (len != state->buflen) { 129552942Sralph regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA; 129656819Sralph readback(regs->asc_cmd); 129752942Sralph asc->script = &asc_scripts[SCRIPT_CONTINUE_IN]; 129852942Sralph return (0); 129952942Sralph } 130052942Sralph return (1); 130152942Sralph } 130252942Sralph 130352942Sralph /* ARGSUSED */ 130452942Sralph static int 130552889Sbostic asc_resume_dma_in(asc, status, ss, ir) 130652889Sbostic register asc_softc_t asc; 130752889Sbostic register int status, ss, ir; 130852889Sbostic { 130952889Sbostic register asc_regmap_t *regs = asc->regs; 131052889Sbostic register State *state = &asc->st[asc->target]; 131152889Sbostic register int len, off; 131252889Sbostic 131352889Sbostic /* setup to finish reading the current chunk */ 131452889Sbostic len = state->dmaresid; 131552889Sbostic off = state->dmalen - len; 131652889Sbostic if ((off & 1) && state->sync_offset) { 131752889Sbostic printf("asc_resume_dma_in: odd xfer dmalen %d len %d off %d\n", 131852889Sbostic state->dmalen, len, off); /* XXX */ 131952889Sbostic regs->asc_res_fifo = state->dmaBufAddr[off]; 132052889Sbostic } 132156819Sralph (*asc->dma_start)(asc, state, state->dmaBufAddr + off, ASCDMA_READ); 132252889Sbostic ASC_TC_PUT(regs, len); 132352942Sralph #ifdef DEBUG 132452942Sralph if (asc_debug > 2) 132552942Sralph printf("asc_resume_dma_in: buflen %d dmalen %d len %d off %d\n", 132652942Sralph state->dmalen, state->buflen, len, off); 132752942Sralph #endif 132852942Sralph 132952942Sralph /* check for next chunk */ 133053080Sralph state->flags |= DMA_IN_PROGRESS; 133152889Sbostic if (state->dmalen != state->buflen) { 133252889Sbostic regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA; 133356819Sralph readback(regs->asc_cmd); 133452942Sralph asc->script = &asc_scripts[SCRIPT_CONTINUE_IN]; 133552889Sbostic return (0); 133652889Sbostic } 133752889Sbostic return (1); 133852889Sbostic } 133952889Sbostic 134052889Sbostic /* ARGSUSED */ 134152889Sbostic static int 134252889Sbostic asc_dma_out(asc, status, ss, ir) 134352889Sbostic register asc_softc_t asc; 134452889Sbostic register int status, ss, ir; 134552889Sbostic { 134652889Sbostic register asc_regmap_t *regs = asc->regs; 134752889Sbostic register State *state = &asc->st[asc->target]; 134852889Sbostic register int len, fifo; 134952889Sbostic 135053080Sralph if (state->flags & DMA_IN_PROGRESS) { 135152889Sbostic /* check to be sure previous chunk was finished */ 135252889Sbostic ASC_TC_GET(regs, len); 135352889Sbostic fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; 135452889Sbostic if (len || fifo) 135552889Sbostic printf("asc_dma_out: buflen %d dmalen %d tc %d fifo %d\n", 135652889Sbostic state->buflen, state->dmalen, len, fifo); /* XXX */ 135752889Sbostic len += fifo; 135852889Sbostic len = state->dmalen - len; 135953080Sralph state->buf += len; 136052889Sbostic state->buflen -= len; 136153080Sralph } 136252889Sbostic 136353080Sralph /* setup for this chunck */ 136453080Sralph len = state->buflen; 136553080Sralph if (len > state->dmaBufSize) 136653080Sralph len = state->dmaBufSize; 136753080Sralph state->dmalen = len; 136853080Sralph bcopy(state->buf, state->dmaBufAddr, len); 136956819Sralph (*asc->dma_start)(asc, state, state->dmaBufAddr, ASCDMA_WRITE); 137052942Sralph ASC_TC_PUT(regs, len); 137152889Sbostic #ifdef DEBUG 137252889Sbostic if (asc_debug > 2) 137352942Sralph printf("asc_dma_out: buflen %d, len %d\n", state->buflen, len); 137452889Sbostic #endif 137552889Sbostic 137652889Sbostic /* check for next chunk */ 137753080Sralph state->flags |= DMA_IN_PROGRESS; 137852889Sbostic if (len != state->buflen) { 137952889Sbostic regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA; 138056819Sralph readback(regs->asc_cmd); 138152942Sralph asc->script = &asc_scripts[SCRIPT_CONTINUE_OUT]; 138252889Sbostic return (0); 138352889Sbostic } 138452889Sbostic return (1); 138552889Sbostic } 138652889Sbostic 138752889Sbostic /* ARGSUSED */ 138852889Sbostic static int 138952889Sbostic asc_last_dma_out(asc, status, ss, ir) 139052889Sbostic register asc_softc_t asc; 139152889Sbostic register int status, ss, ir; 139252889Sbostic { 139352889Sbostic register asc_regmap_t *regs = asc->regs; 139452889Sbostic register State *state = &asc->st[asc->target]; 139552889Sbostic register int len, fifo; 139652889Sbostic 139752889Sbostic ASC_TC_GET(regs, len); 139852889Sbostic fifo = regs->asc_flags & ASC_FLAGS_FIFO_CNT; 139952889Sbostic #ifdef DEBUG 140052889Sbostic if (asc_debug > 2) 140152889Sbostic printf("asc_last_dma_out: buflen %d dmalen %d tc %d fifo %d\n", 140253080Sralph state->buflen, state->dmalen, len, fifo); 140352942Sralph #endif 140452942Sralph if (fifo) { 140552942Sralph len += fifo; 140652942Sralph regs->asc_cmd = ASC_CMD_FLUSH; 140756819Sralph readback(regs->asc_cmd); 140852942Sralph } 140953080Sralph state->flags &= ~DMA_IN_PROGRESS; 141052889Sbostic len = state->dmalen - len; 141152889Sbostic state->buflen -= len; 141252889Sbostic return (1); 141352889Sbostic } 141452889Sbostic 141552889Sbostic /* ARGSUSED */ 141652889Sbostic static int 141752942Sralph asc_resume_out(asc, status, ss, ir) 141852942Sralph register asc_softc_t asc; 141952942Sralph register int status, ss, ir; 142052942Sralph { 142152942Sralph register asc_regmap_t *regs = asc->regs; 142252942Sralph register State *state = &asc->st[asc->target]; 142352942Sralph register int len; 142452942Sralph 142552942Sralph /* setup for this chunck */ 142652942Sralph len = state->buflen; 142752942Sralph if (len > state->dmaBufSize) 142852942Sralph len = state->dmaBufSize; 142952942Sralph state->dmalen = len; 143052942Sralph bcopy(state->buf, state->dmaBufAddr, len); 143156819Sralph (*asc->dma_start)(asc, state, state->dmaBufAddr, ASCDMA_WRITE); 143252942Sralph ASC_TC_PUT(regs, len); 143352942Sralph #ifdef DEBUG 143452942Sralph if (asc_debug > 2) 143552942Sralph printf("asc_resume_out: buflen %d, len %d\n", state->buflen, 143652942Sralph len); 143752942Sralph #endif 143852942Sralph 143952942Sralph /* check for next chunk */ 144053080Sralph state->flags |= DMA_IN_PROGRESS; 144152942Sralph if (len != state->buflen) { 144252942Sralph regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA; 144356819Sralph readback(regs->asc_cmd); 144452942Sralph asc->script = &asc_scripts[SCRIPT_CONTINUE_OUT]; 144552942Sralph return (0); 144652942Sralph } 144752942Sralph return (1); 144852942Sralph } 144952942Sralph 145052942Sralph /* ARGSUSED */ 145152942Sralph static int 145252889Sbostic asc_resume_dma_out(asc, status, ss, ir) 145352889Sbostic register asc_softc_t asc; 145452889Sbostic register int status, ss, ir; 145552889Sbostic { 145652889Sbostic register asc_regmap_t *regs = asc->regs; 145752889Sbostic register State *state = &asc->st[asc->target]; 145852889Sbostic register int len, off; 145952889Sbostic 146052889Sbostic /* setup to finish writing this chunk */ 146152889Sbostic len = state->dmaresid; 146252889Sbostic off = state->dmalen - len; 146352889Sbostic if (off & 1) { 146452889Sbostic printf("asc_resume_dma_out: odd xfer dmalen %d len %d off %d\n", 146552889Sbostic state->dmalen, len, off); /* XXX */ 146652889Sbostic regs->asc_fifo = state->dmaBufAddr[off]; 146752889Sbostic off++; 146852889Sbostic len--; 146952889Sbostic } 147056819Sralph (*asc->dma_start)(asc, state, state->dmaBufAddr + off, ASCDMA_WRITE); 147152889Sbostic ASC_TC_PUT(regs, len); 147252942Sralph #ifdef DEBUG 147352942Sralph if (asc_debug > 2) 147452942Sralph printf("asc_resume_dma_out: buflen %d dmalen %d len %d off %d\n", 147552942Sralph state->dmalen, state->buflen, len, off); 147652942Sralph #endif 147752942Sralph 147852942Sralph /* check for next chunk */ 147953080Sralph state->flags |= DMA_IN_PROGRESS; 148052889Sbostic if (state->dmalen != state->buflen) { 148152889Sbostic regs->asc_cmd = ASC_CMD_XFER_INFO | ASC_CMD_DMA; 148256819Sralph readback(regs->asc_cmd); 148352942Sralph asc->script = &asc_scripts[SCRIPT_CONTINUE_OUT]; 148452889Sbostic return (0); 148552889Sbostic } 148652889Sbostic return (1); 148752889Sbostic } 148852889Sbostic 148952889Sbostic /* ARGSUSED */ 149052889Sbostic static int 149152889Sbostic asc_sendsync(asc, status, ss, ir) 149252889Sbostic register asc_softc_t asc; 149352889Sbostic register int status, ss, ir; 149452889Sbostic { 149552889Sbostic register asc_regmap_t *regs = asc->regs; 149653080Sralph register State *state = &asc->st[asc->target]; 149752889Sbostic 149853080Sralph /* send the extended synchronous negotiation message */ 149952889Sbostic regs->asc_fifo = SCSI_EXTENDED_MSG; 150052889Sbostic MachEmptyWriteBuffer(); 150152889Sbostic regs->asc_fifo = 3; 150252889Sbostic MachEmptyWriteBuffer(); 150352889Sbostic regs->asc_fifo = SCSI_SYNCHRONOUS_XFER; 150452889Sbostic MachEmptyWriteBuffer(); 150552889Sbostic regs->asc_fifo = SCSI_MIN_PERIOD; 150652889Sbostic MachEmptyWriteBuffer(); 150752889Sbostic regs->asc_fifo = ASC_MAX_OFFSET; 150853080Sralph /* state to resume after we see the sync reply message */ 150953080Sralph state->script = asc->script + 2; 151053080Sralph state->msglen = 0; 151152889Sbostic return (1); 151252889Sbostic } 151352889Sbostic 151452889Sbostic /* ARGSUSED */ 151552889Sbostic static int 151652889Sbostic asc_replysync(asc, status, ss, ir) 151752889Sbostic register asc_softc_t asc; 151852889Sbostic register int status, ss, ir; 151952889Sbostic { 152052889Sbostic register asc_regmap_t *regs = asc->regs; 152152889Sbostic register State *state = &asc->st[asc->target]; 152252889Sbostic 152352889Sbostic #ifdef DEBUG 152452889Sbostic if (asc_debug > 2) 152552889Sbostic printf("asc_replysync: %x %x\n", 152656819Sralph asc_to_scsi_period[state->sync_period] * asc->tb_ticks, 152752889Sbostic state->sync_offset); 152852889Sbostic #endif 152952889Sbostic /* send synchronous transfer in response to a request */ 153052889Sbostic regs->asc_fifo = SCSI_EXTENDED_MSG; 153152889Sbostic MachEmptyWriteBuffer(); 153252889Sbostic regs->asc_fifo = 3; 153352889Sbostic MachEmptyWriteBuffer(); 153452889Sbostic regs->asc_fifo = SCSI_SYNCHRONOUS_XFER; 153552889Sbostic MachEmptyWriteBuffer(); 153656819Sralph regs->asc_fifo = asc_to_scsi_period[state->sync_period] * asc->tb_ticks; 153752889Sbostic MachEmptyWriteBuffer(); 153852889Sbostic regs->asc_fifo = state->sync_offset; 153952889Sbostic regs->asc_cmd = ASC_CMD_XFER_INFO; 154056819Sralph readback(regs->asc_cmd); 154152889Sbostic 154252889Sbostic /* return to the appropriate script */ 154352889Sbostic if (!state->script) { 154452889Sbostic #ifdef DEBUG 154552889Sbostic asc_DumpLog("asc_replsync"); 154652889Sbostic #endif 154752889Sbostic panic("asc_replysync"); 154852889Sbostic } 154952889Sbostic asc->script = state->script; 155052889Sbostic state->script = (script_t *)0; 155152889Sbostic return (0); 155252889Sbostic } 155352889Sbostic 155452889Sbostic /* ARGSUSED */ 155552889Sbostic static int 155652942Sralph asc_msg_in(asc, status, ss, ir) 155752889Sbostic register asc_softc_t asc; 155852889Sbostic register int status, ss, ir; 155952889Sbostic { 156052889Sbostic register asc_regmap_t *regs = asc->regs; 156152889Sbostic register State *state = &asc->st[asc->target]; 156252889Sbostic register int msg; 156352889Sbostic int i; 156452889Sbostic 156552889Sbostic /* read one message byte */ 156652889Sbostic msg = regs->asc_fifo; 156752889Sbostic #ifdef DEBUG 156852889Sbostic if (asc_logp == asc_log) 156952889Sbostic asc_log[NLOG - 1].msg = msg; 157052889Sbostic else 157152889Sbostic asc_logp[-1].msg = msg; 157252889Sbostic #endif 157352889Sbostic 157452889Sbostic /* check for multi-byte message */ 157552889Sbostic if (state->msglen != 0) { 157652889Sbostic /* first byte is the message length */ 157752889Sbostic if (state->msglen < 0) { 157852889Sbostic state->msglen = msg; 157952889Sbostic return (1); 158052889Sbostic } 158152889Sbostic if (state->msgcnt >= state->msglen) 158252889Sbostic goto abort; 158352889Sbostic state->msg_in[state->msgcnt++] = msg; 158452889Sbostic 158552889Sbostic /* did we just read the last byte of the message? */ 158652889Sbostic if (state->msgcnt != state->msglen) 158752889Sbostic return (1); 158852889Sbostic 158952889Sbostic /* process an extended message */ 159052889Sbostic #ifdef DEBUG 159152889Sbostic if (asc_debug > 2) 159252942Sralph printf("asc_msg_in: msg %x %x %x\n", 159352889Sbostic state->msg_in[0], 159452889Sbostic state->msg_in[1], 159552889Sbostic state->msg_in[2]); 159652889Sbostic #endif 159752889Sbostic switch (state->msg_in[0]) { 159852889Sbostic case SCSI_SYNCHRONOUS_XFER: 159952889Sbostic state->flags |= DID_SYNC; 160052889Sbostic state->sync_offset = state->msg_in[2]; 160152889Sbostic 160252889Sbostic /* convert SCSI period to ASC period */ 160356819Sralph i = state->msg_in[1] / asc->tb_ticks; 160456819Sralph if (i < asc->min_period) 160556819Sralph i = asc->min_period; 160656819Sralph else if (i >= asc->max_period) { 160752889Sbostic /* can't do sync transfer, period too long */ 160852889Sbostic printf("asc%d: SCSI device %d: sync xfer period too long (%d)\n", 160952889Sbostic asc - asc_softc, asc->target, i); 161056819Sralph i = asc->max_period; 161152889Sbostic state->sync_offset = 0; 161252889Sbostic } 161356819Sralph if ((i * asc->tb_ticks) != state->msg_in[1]) 161452889Sbostic i++; 161552889Sbostic state->sync_period = i & 0x1F; 161652889Sbostic 161752889Sbostic /* 161852889Sbostic * If this is a request, check minimums and 161952889Sbostic * send back an acknowledge. 162052889Sbostic */ 162152889Sbostic if (!(state->flags & TRY_SYNC)) { 162252889Sbostic regs->asc_cmd = ASC_CMD_SET_ATN; 162356819Sralph readback(regs->asc_cmd); 162452889Sbostic 162556819Sralph if (state->sync_period < asc->min_period) 162652889Sbostic state->sync_period = 162756819Sralph asc->min_period; 162852889Sbostic if (state->sync_offset > ASC_MAX_OFFSET) 162952889Sbostic state->sync_offset = 163052889Sbostic ASC_MAX_OFFSET; 163152889Sbostic asc->script = &asc_scripts[SCRIPT_REPLY_SYNC]; 163252889Sbostic regs->asc_syn_p = state->sync_period; 163356819Sralph readback(regs->asc_syn_p); 163452889Sbostic regs->asc_syn_o = state->sync_offset; 163556819Sralph readback(regs->asc_syn_o); 163652889Sbostic regs->asc_cmd = ASC_CMD_MSG_ACPT; 163756819Sralph readback(regs->asc_cmd); 163852889Sbostic return (0); 163952889Sbostic } 164052889Sbostic 164152889Sbostic regs->asc_syn_p = state->sync_period; 164256819Sralph readback(regs->asc_syn_p); 164352889Sbostic regs->asc_syn_o = state->sync_offset; 164456819Sralph readback(regs->asc_syn_o); 164552889Sbostic goto done; 164652889Sbostic 164752889Sbostic default: 164852889Sbostic printf("asc%d: SCSI device %d: rejecting extended message 0x%x\n", 164952889Sbostic asc - asc_softc, asc->target, 165052889Sbostic state->msg_in[0]); 165152889Sbostic goto reject; 165252889Sbostic } 165352889Sbostic } 165452889Sbostic 165552889Sbostic /* process first byte of a message */ 165652889Sbostic #ifdef DEBUG 165752889Sbostic if (asc_debug > 2) 165852942Sralph printf("asc_msg_in: msg %x\n", msg); 165952889Sbostic #endif 166052889Sbostic switch (msg) { 166152889Sbostic #if 0 166252889Sbostic case SCSI_MESSAGE_REJECT: 166352889Sbostic printf(" did not like SYNCH xfer "); /* XXX */ 166452889Sbostic state->flags |= DID_SYNC; 166552889Sbostic regs->asc_cmd = ASC_CMD_MSG_ACPT; 166656819Sralph readback(regs->asc_cmd); 166752889Sbostic status = asc_wait(regs, ASC_CSR_INT); 166852889Sbostic ir = regs->asc_intr; 166952889Sbostic /* some just break out here, some dont */ 167052889Sbostic if (ASC_PHASE(status) == ASC_PHASE_MSG_OUT) { 167152889Sbostic regs->asc_fifo = SCSI_ABORT; 167252889Sbostic regs->asc_cmd = ASC_CMD_XFER_INFO; 167356819Sralph readback(regs->asc_cmd); 167452889Sbostic status = asc_wait(regs, ASC_CSR_INT); 167552889Sbostic ir = regs->asc_intr; 167652889Sbostic } 167752889Sbostic if (ir & ASC_INT_DISC) { 167852889Sbostic asc_end(asc, status, 0, ir); 167952889Sbostic return (0); 168052889Sbostic } 168152889Sbostic goto status; 168252889Sbostic #endif 168352889Sbostic 168452889Sbostic case SCSI_EXTENDED_MSG: /* read an extended message */ 168552889Sbostic /* setup to read message length next */ 168652889Sbostic state->msglen = -1; 168752889Sbostic state->msgcnt = 0; 168852889Sbostic return (1); 168952889Sbostic 169052889Sbostic case SCSI_NO_OP: 169152889Sbostic break; 169252889Sbostic 169352889Sbostic case SCSI_SAVE_DATA_POINTER: 169452889Sbostic /* expect another message */ 169552889Sbostic return (1); 169652889Sbostic 169752889Sbostic case SCSI_RESTORE_POINTERS: 169852889Sbostic /* 169952889Sbostic * Need to do the following if resuming synchonous data in 170052889Sbostic * on an odd byte boundary. 170152889Sbostic regs->asc_cnfg2 |= ASC_CNFG2_RFB; 170252889Sbostic */ 170352889Sbostic break; 170452889Sbostic 170552889Sbostic case SCSI_DISCONNECT: 170652889Sbostic if (state->flags & DISCONN) 170752889Sbostic goto abort; 170852889Sbostic state->flags |= DISCONN; 170952942Sralph regs->asc_cmd = ASC_CMD_MSG_ACPT; 171056819Sralph readback(regs->asc_cmd); 171152942Sralph asc->script = &asc_scripts[SCRIPT_DISCONNECT]; 171252942Sralph return (0); 171352889Sbostic 171452889Sbostic default: 171552889Sbostic printf("asc%d: SCSI device %d: rejecting message 0x%x\n", 171652889Sbostic asc - asc_softc, asc->target, msg); 171752889Sbostic reject: 171852889Sbostic /* request a message out before acknowledging this message */ 171952889Sbostic state->msg_out = SCSI_MESSAGE_REJECT; 172052889Sbostic regs->asc_cmd = ASC_CMD_SET_ATN; 172156819Sralph readback(regs->asc_cmd); 172252889Sbostic } 172352889Sbostic 172452889Sbostic done: 172552889Sbostic /* return to original script */ 172652889Sbostic regs->asc_cmd = ASC_CMD_MSG_ACPT; 172756819Sralph readback(regs->asc_cmd); 172852889Sbostic if (!state->script) { 172952889Sbostic abort: 173052889Sbostic #ifdef DEBUG 173152942Sralph asc_DumpLog("asc_msg_in"); 173252889Sbostic #endif 173352942Sralph panic("asc_msg_in"); 173452889Sbostic } 173552889Sbostic asc->script = state->script; 173652889Sbostic state->script = (script_t *)0; 173752889Sbostic return (0); 173852889Sbostic } 173952889Sbostic 174052942Sralph /* ARGSUSED */ 174152942Sralph static int 174252942Sralph asc_disconnect(asc, status, ss, ir) 174352942Sralph register asc_softc_t asc; 174452942Sralph register int status, ss, ir; 174552942Sralph { 174652942Sralph register State *state = &asc->st[asc->target]; 174752942Sralph 1748*58658Sralph #ifdef DIAGNOSTIC 1749*58658Sralph if (!(state->flags & DISCONN)) { 1750*58658Sralph printf("asc_disconnect: device %d: DISCONN not set!\n", 1751*58658Sralph asc->target); 1752*58658Sralph } 1753*58658Sralph #endif 175452942Sralph asc->target = -1; 175552942Sralph asc->state = ASC_STATE_RESEL; 175652942Sralph return (1); 175752942Sralph } 175852942Sralph 175956819Sralph /* 176056819Sralph * DMA handling routines. For a turbochannel device, just set the dmar 176156819Sralph * for the I/O ASIC, handle the actual DMA interface. 176256819Sralph */ 176356819Sralph static void 176456819Sralph tb_dma_start(asc, state, cp, flag) 176556819Sralph asc_softc_t asc; 176656819Sralph State *state; 176756819Sralph caddr_t cp; 176856819Sralph int flag; 176956819Sralph { 177056819Sralph 177156819Sralph if (flag == ASCDMA_WRITE) 177256819Sralph *asc->dmar = ASC_DMAR_WRITE | ASC_DMA_ADDR(cp); 177356819Sralph else 177456819Sralph *asc->dmar = ASC_DMA_ADDR(cp); 177556819Sralph } 177656819Sralph 177756819Sralph static void 177856819Sralph tb_dma_end(asc, state, flag) 177956819Sralph asc_softc_t asc; 178056819Sralph State *state; 178156819Sralph int flag; 178256819Sralph { 178356819Sralph 178456819Sralph } 178556819Sralph 178656819Sralph static void 178756819Sralph asic_dma_start(asc, state, cp, flag) 178856819Sralph asc_softc_t asc; 178956819Sralph State *state; 179056819Sralph caddr_t cp; 179156819Sralph int flag; 179256819Sralph { 179356819Sralph register volatile u_int *ssr = (volatile u_int *) 179457233Sralph ASIC_REG_CSR(asic_base); 179556819Sralph u_int phys, nphys; 179656819Sralph 179756819Sralph /* stop DMA engine first */ 179856819Sralph *ssr &= ~ASIC_CSR_DMAEN_SCSI; 1799*58658Sralph *((volatile int *)ASIC_REG_SCSI_SCR(asic_base)) = 0; 180056819Sralph 180156819Sralph phys = MACH_CACHED_TO_PHYS(cp); 180256819Sralph cp = (caddr_t)pmax_trunc_page(cp + NBPG); 180356819Sralph nphys = MACH_CACHED_TO_PHYS(cp); 180456819Sralph 180556819Sralph asc->dma_next = cp; 180656819Sralph asc->dma_xfer = state->dmalen - (nphys - phys); 180756819Sralph 180857233Sralph *(volatile int *)ASIC_REG_SCSI_DMAPTR(asic_base) = 180956819Sralph ASIC_DMA_ADDR(phys); 181057233Sralph *(volatile int *)ASIC_REG_SCSI_DMANPTR(asic_base) = 181156819Sralph ASIC_DMA_ADDR(nphys); 181256819Sralph if (flag == ASCDMA_READ) 181356819Sralph *ssr |= ASIC_CSR_SCSI_DIR | ASIC_CSR_DMAEN_SCSI; 181456819Sralph else 181556819Sralph *ssr = (*ssr & ~ASIC_CSR_SCSI_DIR) | ASIC_CSR_DMAEN_SCSI; 181656819Sralph MachEmptyWriteBuffer(); 181756819Sralph } 181856819Sralph 181956819Sralph static void 182056819Sralph asic_dma_end(asc, state, flag) 182156819Sralph asc_softc_t asc; 182256819Sralph State *state; 182356819Sralph int flag; 182456819Sralph { 182556819Sralph register volatile u_int *ssr = (volatile u_int *) 182657233Sralph ASIC_REG_CSR(asic_base); 182756819Sralph int nb; 182856819Sralph 182956819Sralph *ssr &= ~ASIC_CSR_DMAEN_SCSI; 183057233Sralph *((volatile int *)ASIC_REG_SCSI_DMAPTR(asic_base)) = -1; 183157233Sralph *((volatile int *)ASIC_REG_SCSI_DMANPTR(asic_base)) = -1; 183256819Sralph MachEmptyWriteBuffer(); 183356819Sralph 183456819Sralph if (flag == ASCDMA_READ) { 183556819Sralph MachFlushDCache(MACH_PHYS_TO_CACHED( 183656819Sralph MACH_UNCACHED_TO_PHYS(state->dmaBufAddr)), state->dmalen); 183757233Sralph if (nb = *((int *)ASIC_REG_SCSI_SCR(asic_base))) { 183856819Sralph /* pick up last upto6 bytes, sigh. */ 183956819Sralph register u_short *to; 184056819Sralph register int w; 184156819Sralph 184256819Sralph /* Last byte really xferred is.. */ 184356819Sralph to = (u_short *)(state->dmaBufAddr + state->dmalen - (nb << 1)); 184457233Sralph w = *(int *)ASIC_REG_SCSI_SDR0(asic_base); 184556819Sralph *to++ = w; 184656819Sralph if (--nb > 0) { 184756819Sralph w >>= 16; 184856819Sralph *to++ = w; 184956819Sralph } 185056819Sralph if (--nb > 0) { 185157233Sralph w = *(int *)ASIC_REG_SCSI_SDR1(asic_base); 185256819Sralph *to++ = w; 185356819Sralph } 185456819Sralph } 185556819Sralph } 185656819Sralph } 185756819Sralph 185857233Sralph #ifdef notdef 185956819Sralph /* 186056819Sralph * Called by asic_intr() for scsi dma pointer update interrupts. 186156819Sralph */ 186256819Sralph void 186356819Sralph asc_dma_intr() 186456819Sralph { 186556819Sralph asc_softc_t asc = &asc_softc[0]; 186656819Sralph u_int next_phys; 186756819Sralph 186856819Sralph asc->dma_xfer -= NBPG; 186956819Sralph if (asc->dma_xfer <= -NBPG) { 187056819Sralph volatile u_int *ssr = (volatile u_int *) 187157233Sralph ASIC_REG_CSR(asic_base); 187256819Sralph *ssr &= ~ASIC_CSR_DMAEN_SCSI; 187356819Sralph } else { 187456819Sralph asc->dma_next += NBPG; 187556819Sralph next_phys = MACH_CACHED_TO_PHYS(asc->dma_next); 187656819Sralph } 187757233Sralph *(volatile int *)ASIC_REG_SCSI_DMANPTR(asic_base) = 187856819Sralph ASIC_DMA_ADDR(next_phys); 187956819Sralph MachEmptyWriteBuffer(); 188056819Sralph } 188157233Sralph #endif 188256819Sralph 188352889Sbostic #ifdef DEBUG 188452889Sbostic asc_DumpLog(str) 188552889Sbostic char *str; 188652889Sbostic { 188752889Sbostic register struct asc_log *lp; 188852889Sbostic register u_int status; 188952889Sbostic 189052889Sbostic printf("asc: %s: cmd %x bn %d cnt %d\n", str, asc_debug_cmd, 189152889Sbostic asc_debug_bn, asc_debug_sz); 189258570Sralph lp = asc_logp; 189358570Sralph do { 189452889Sbostic status = lp->status; 1895*58658Sralph printf("asc%d tgt %d status %x ss %x ir %x cond %d:%x msg %x resid %d\n", 189652889Sbostic status >> 24, 189752889Sbostic lp->target, 189852889Sbostic (status >> 16) & 0xFF, 189952889Sbostic (status >> 8) & 0xFF, 190052889Sbostic status & 0XFF, 190152889Sbostic lp->state, 190252889Sbostic asc_scripts[lp->state].condition, 1903*58658Sralph lp->msg, lp->resid); 190452889Sbostic if (++lp >= &asc_log[NLOG]) 190552889Sbostic lp = asc_log; 190658570Sralph } while (lp != asc_logp); 190752889Sbostic } 190852889Sbostic #endif 190952889Sbostic 191052889Sbostic #endif /* NASC > 0 */ 1911