/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: ssm_scsi.h 1.10 90/05/04 $"

#ifndef	_SYS_SSM_SCSI_H_

/*
 * ssm_scsi.h
 *      SCSI SSM definitions
 */

/* $Log:	ssm_scsi.h,v $
 */

#include "sys/autoconf.h"
#include "sys/scsi.h"
#include "sys/scsi_adapter.h"

/*
 * Each SSM SCSI device has a table of command blocks
 * (CBs) associated with it through which the host and
 * SSM communicate.  For each SCSI device, the host 
 * informs the SSM where a device's CBs are (via slic).
 * The host simultaneously communicates the SCSI address 
 * (target adapter and logical unit number) and the SCSI
 * interface number on that SSM through an init_cb located
 * at that address.  The SSM then returns an id number
 * for that device in the init_cb.  
 *
 * The id number returned by the SSM must is used to
 * generate appropriate interrupt vectors to the SSM
 * for that device in the future.
 */

/*
 * Limit definitions.
 */
#define	NCBPERSCSI	2		/* CBs per logical unit */
#define	NCBSCSISHFT	1		/* log2(NCBPERSCSI) */
#define NDEVSCSI	16		/* Max devices per SCSI bus */
#define SC_MAXSCSI	1		/* Max SCSI bus's per SSM */
#define SC_MAXID	(SC_MAXSCSI * NDEVSCSI - 1)
					/* Max SSM SCSI device id number */

#define SSM_SCSI_DEVNO(targ,lun)	(((targ) << 3) | lun)
					/* Device address on the SCSI bus */

/*
 * Given a device's SSM id and CB number, compute its
 * SSM SLIC interrupt vector.
 */
#define	SCVEC(id,cb)	((id) << NCBSCSISHFT | (cb))

/*
 * Given a base vector for a device's CBs and a CB 
 * number, compute its host SLIC interrupt vector.
 */
#define	SC_HOSTVEC(basevec,cb)	((basevec) + (cb))

/*
 * Given an SSM SLIC interrupt vector, extract a device 
 * id number and CB number from it.
 */
#define	SCVEC_ID(vec)	((vec) >> NCBSCSISHFT & SC_MAXID)
#define	SCVEC_CB(vec)	((vec) & NCBPERSCSI - 1)

/*
 * SLIC interrupt bin for sending SCSI commands to SSM.
 */
#define	SCSI_BIN	0x06		/* SLIC bin for SCSI CBs */

/*
 * Special SSM SLIC interrupt vectors for communicating
 * the location of a device's CBs, the first of which
 * is also used as an initialization CB.
 *
 * SCSI_PROBEVEC is used during driver probe time so the
 * SSM can re-use its device id, etc., conserving memory.
 * SCSI_BOOTVEC is used once per device at boot time to
 * inform the SSM to generate a unique device id.
 */
#define	SCB_PROBEVEC	0xFE		/* Vector sent w/init CB during probe */
#define	SCB_BOOTVEC	0xFF		/* Vector sent w/init CB during boot */

#define SCSIMAXCMD		12	/* Maximum SCSI command length */

struct shpart {
	/*
	 * These variables are read by the FW for 
	 * every command.  They are not updated by 
	 * the FW and therefore do not need to be 
	 * copied back when the command completes.
	 */
	ulong	cb_sense;		/* SCSI request sense buffer address */
	unchar	cb_slen;		/* SCSI Request Sense buffer length */
	unchar	cb_cmd;			/* SSM command for this CB */
	unchar	cb_reserved0[1];	/* Reserved for future use; MBZ */
	unchar	cb_clen;		/* SCSI command length (6, 10, 12) */
	unchar	cb_scmd[SCSIMAXCMD];	/* SCSI command */
	unchar	cb_reserved1[4];	/* Reserved for future use; MBZ */
	/*
	 * These variables are initialized by SW, 
	 * used and updated by FW. After the FW is 
	 * instructed to execute the CB, the host 
	 * must not use that CB until cb_compcode 
	 * is non-zero.
	 */
	ulong	cb_addr;		/* Virtual addr if cb_iovec non-zero
					 * else physical address */
	ulong	*cb_iovec;		/* If non-zero, physical address of
					 * list of iovectors */
	ulong	cb_count;		/* Transfer count */
	unchar	cb_status;		/* SCSI status from cb_scmd */
	unchar	cb_reserved2[2];	/* Reserved for future use; MBZ */
	unchar	cb_compcode;		/* SSM completion code */
};

/*
 * Software only part of the SCSI CB. 
 * Not read by the firmware.
 */
struct softw {
	struct buf	*cb_bp;		/* Pointer to buf header */
	ulong	*cb_iovstart;		/* Start of cb_iovec buffer */
	ushort	cb_errcnt;		/* Number of retries */
	unchar	cb_unit_index;		/* SSM SLIC vector for this CB */
	unchar	cb_scsi_device;		/* This device's SCSI address */
	ulong	cb_state;		/* Current job state */
	/* 
	 * The following field must not be greater than 
	 * 8 bytes to preserve the desired cb size.  
	 * Driver dependent declarations may be inserted.
	 */
	union {
		unchar	cbs_reserved3[8]; /* Reserved for future use */
		struct {		/* For scsi disk driver */
			ulong	cbd_data;
			ushort cbd_iovnum;
			unchar  cbd_slic;
		} cbs_wd;
	} sw_un;
};

/* 
 * Abreviations for union entries.
 */
#define	cb_reserved3		sw_un.cbs_reserved3
#define cb_data			sw_un.cbs_wd.cbd_data
#define cb_iovnum		sw_un.cbs_wd.cbd_iovnum
#define cb_slic			sw_un.cbs_wd.cbd_slic
#ifdef ONLINE_FORMAT
#define cb_action		sw_un.cbs_wd.cbd_action
#endif /* ONLINE_FORMAT */

/*
 * If you change the structure of a SCSI CB make sure the constant
 * definitions below are correct.
 * The relative adrress of the SSM command field must be the same
 * in the structure scsi_cb as in the structure scsi_init_cb
 */
struct scsi_cb {
	struct shpart sh;		/* FW/SW shared part */
	struct softw sw;		/* Software only part */
};

/*
 *
 * Clearing of a SCSI CB:
 * 	bzero(SWBZERO(&SequentScsiCb), SWBZERO_SIZE);
 *
 * Firmware write to Sequent memory:
 *
 *	bcopy( FWWRITE(&SsmScsiCb), FWWRITE(&SequentScsiCb),
 *		FWWRITE_SIZE );
 *	ssmPic.flush();
 *
 * Since the write operation is a 16-byte multiple, 
 * the PIC flush should not have to wait.
 */

#define	SCB_SHSIZ	40		/* Size of shared portion */
#define	SCB_SWSIZ	24		/* Size of S/W-only portion */
#define FWWRITE_SIZE	((uint) &((struct shpart *) 0)->cb_compcode + \
	sizeof(unchar)) - (uint) &((struct shpart *) 0)->cb_addr
#define FWREAD_ONLY	sizeof(struct shpart) - FWWRITE_SIZE
#define FWWRITE(p)	((char *) ((uint) (p) + FWREAD_ONLY))
#define SWBZERO_SIZE	((uint) &((struct shpart *) 0)->cb_compcode + \
	sizeof(unchar)) - (uint) &((struct shpart *) 0)->cb_cmd
#define SWBZERO(p)	((char *) ((uint) (p) + 0x05))

struct scsi_init_cb {
	/* Start of shared part */
	ulong	icb_pagesize;		/* Page size in bytes */
	unchar	icb_flags;		/* Flags describing the device */
	unchar	icb_cmd;		/* Command */
	unchar	icb_sdest;		/* SLIC dest for intrs */
	unchar	icb_svec;		/* SLIC vector for intrs */
	unchar	icb_scmd;		/* SLIC cmd for intrs */
	unchar	icb_scsi;		/* SCSI interface number on the SSM */
	unchar	icb_target;		/* Target adapter number on the SCSI */
	unchar	icb_lunit;		/* Logical unit number on the SCSI */
	unchar	icb_control;		/* Control byte for request-sense 
					 * commands generated by the SSM */
	unchar	icb_reserved1[3];	/* Reserved */
	long	icb_id;			/* i.d. number from SSM upon completion.
					 * Error occurred if less than zero. */ 
	ulong	icb_reserved2[4];	/* Reserved */
	unchar	icb_reserved3[3];	/* Reserved */
	unchar	icb_compcode;		/* SSM completion code */

	/* Start of sw-only part */
	ulong	icb_swreserved[SCB_SWSIZ/sizeof(ulong)];	/* Reserved */
};

/*
 * cb_flags 
 * These flags define information passed from the device
 * driver to the SSM firmware.  The flags will be information
 * the firmware may want to know about this device.
 */
#define SCBF_ASYNCH	0x01		/* Device only transfers data with
					 * asynchronous SCSI protocol */

/*
 * cb_cmd command codes
 *
 * Commands are composed of flag bits in the high nibble
 * and command bits in the low nibble.
 */
#define	SCB_IENABLE	0x80		/* Enable interrupts */
#define	SCB_CMD_MASK	0x0F		/* Command bits */
#define	SCB_INITDEV	0x00		/* Init SCSI device's state */
#define	SCB_NOP		0x01		/* Do nothing */
#define	SCB_READ	0x02		/* Do read from the SCSI bus */
#define	SCB_WRITE	0x03		/* Do write to SCSI bus */
#define	SCB_MAX_CMD	0x03		/* Max command number */

/* 
 * Extract SCSI command number.
 */
#define	SCB_CMD(cmd)	((cmd) & SCB_CMD_MASK)

/* 
 * SSM SCSI CB completion codes.
 */
#define	SCB_BUSY	0x00		/* No completion code yet */
#define	SCB_OK		0x01		/* Completed OK */
#define	SCB_BAD_CB	0x02		/* CB invalid */
#define	SCB_NO_TARGET	0x03		/* No target adapter response */
#define	SCB_SCSI_ERR	0x04		/* SCSI protocol err */

/* 
 * The following structure is used for returning 
 * initialization information about a SCSI device
 * from init_ssm_scsi_dev().
 */
struct scb_init {
	unchar	si_mode;		/* Initialization mode for device */
	unchar	si_ssm_slic;		/* SLIC id of the SSM board */
	unchar	si_scsi;		/* SCSI interface on the SSM board */
	unchar	si_target;		/* Target adapter number of device */
	unchar	si_lunit;		/* Logical unit number of device */
	unchar	si_host_bin;		/* SLIC bin to interrupt device at */
	unchar	si_host_basevec;	/* Base of sequential SLIC vectors for 
					 * device; one per scsi_cb. */
	unchar	si_control;		/* Control byte for request-sense 
					 * commands generated by the SSM */
	long	si_id;			/* I.D. number returned by SSM */
	struct	scsi_cb	*si_cbs;	/* Address of CBs for this device */
	unchar	si_flags;		/* Value for init cb icb_flags */
};

/* Values for icb->si_mode. */
#define SCB_PROBE	0		/* Currently probing devices; may
					 * reuse CBs and the device id */
#define SCB_BOOT	1		/* Booting the device; allocate
					 * unique device id and CBs */

/*
 * Each bootable SSM SCSI device unit will be
 * have its 'map' origin field set to address
 * an allocated structure as defined below.
 * It is used by the SSM SCSI's DMA mapping 
 * functions for that device.  It addresses a 
 * table of DMA maps and a corresponding table of 
 * i.d. codes indicating which maps are/aren't
 * in use.  The i.d. used to allocate maps and 
 * map a transfer must be used to deallocate those
 * maps and unmap the transfer.
 */
struct ssm_scsi_dma {
	ulong *id;			/* Identifiers for mapping requestor */
	ulong *map;			/* SSM SCSI dmap map table */
};

/* 
 * Each available SSM SCSI device will have one
 * of the following structures allocated for it,
 * the address of which is stored in the 'adapt' 
 * field of its struct gen_scsi_device.  This 
 * structure is used exclusively by SSM SCSI code
 * to control adapter resources and provide 
 * interrupt mapping.
 */
struct ssm_scsi_adapt {
	lock_t lock;			/* Access syncronization */
	int avail;			/* Number of CBs available for use */
	int next;			/* Index of next available CB */
	struct scsi_cb *cb;		/* SSM SCSI CB's for the device. */
	unchar bin;			/* SLIC bin for device's interrupts */
	unchar basevec;			/* Base SLIC interrupt vector in 'bin' 
					 * for this device */
	bool_t booted;			/* Flag to indicate when device is
					 * fully booted and interruptable */
};

/*
 * The following value is placed in dev->ident
 * at probe time to tip off ssm_scsi_polled_cmd()
 * that SSM SCSI resources are not fully allocated
 * for this device and that it is being probed.
 * The address of a zeroed out ssm_scsi_adapt
 * structure will be placed in dev->adapt also.
 */
#define SCA_PROBE_IDENT		0xffffffff

/*
 * A global array of the following structure
 * is used at device initialization time to gather
 * information about interrupt mapping (one per bin).  
 *
 * Interrupts from SSM SCSI devices are initially 
 * delivered to an SSM SCSI specific handler.
 * The handler locates the intended device and
 * corresponding request, then summarizes its 
 * termination status and releases SSM SCSI specific 
 * resources prior to invoking the device's handler.
 */ 
struct ssm_scsi_intr {
	bool_t in_use;			/* True when there are SSM SCSI
					 * interrupts mapped to this bin. */
	unchar basevec;			/* Smallest SSM SCSI host interrupt 
					 * vector mapped to this bin. */
	unchar topvec;			/* Largest host SSM SCSI interrupt
					 * vector mapped to this bin. */
	scsidev_t **dev;		/* Array of dev addresses, one per
					 * vector mapped to this bin in
					 * the range basevec...topvec. */
};

/*
 * The following macros determine if the 
 * SCSI device associated with a given
 * ssm_dev structure has a wildcarded address.
 */
#define	SSM_WILD_BUS(dev)     ((dev)->gen.busnum < 0)
#define SSM_WILD_UNIT(dev)    ((dev)->gen.unit < 0)
#define	SSM_WILD_TARG(dev)    ((dev)->gen.config->space < 0 || \
					SSM_WILD_UNIT(dev))
#define	SSM_WILD_LUNIT(dev)   ((dev)->gen.config->dev_addr < 0 || \
					SSM_WILD_UNIT(dev))
#define	SSM_SCSI_WILD(dev)	(SSM_WILD_BUS(dev) || \
				 SSM_WILD_TARG(dev) || SSM_WILD_LUNIT(dev))

#ifdef INKERNEL

/*
 * The following are global interfaces from ssm_scsi.c.
 */
extern void init_ssm_scsi_dev();
extern ulong scb_buf_iovects();

#endif /* INKERNEL */

#define	_SYS_SSM_SCSI_H_
#endif	/* _SYS_SSM_SCSI_H_ */
