/*
	@(#)cwc.c	1.10	(2/20/85 09:54:15)
*/
#include "standalone.h"
#include "sys/config.h"

/*
#define	DEBUG	1
*/

#ifdef PM68K
#define MBUSMEMS	0x040000		/* Start of MULTIBUS memory. */
#endif

#define	CWCCMDP_VA	(MBIOBASE + 0x00a0)	/* I/O ports base address.   */
#define CWCLITES_VA	(MBIOBASE + 0x00F0)	/* Disk lights base address. */
#ifdef CD68K
#define	CWCDBUF_VA      (cwcdbuf)		/* Multibus buffer address.  */
#define	CWCCPBP_VA      ((char *) &cpbbuf)	/* Command Parameter Block.  */
#define	CWCSTBP_VA	((char *) &stbbuf)	/* Status Block.             */
#endif
#ifdef PM68K
#define	CWCDBUF_VA      (MBUSMEMS + 0x0600)	/* Multibus buffer address.  */
#define	CWCCPBP_VA      (MBUSMEMS + 0x0800)	/* Command Parameter Block.  */
#define	CWCSTBP_VA	(MBUSMEMS + 0x0900)	/* Status Block.             */
#endif

#ifdef CD68K
#define vtop(p)		((int) (p))
#endif
#ifdef PM68K
#define	vtop(p)		(((int)(p))-MBUSMEMS)
#endif

#define	CWC_TIMEOUT	500000

#define	CWC_BUSY	0x80		/* Controller is busy.		*/
#define	CWC_ISINT	0x40		/* Controller IS interrupting.	*/
#define	CWC_INTEN	0x80		/* Interrupt enable.		*/
#define	CWC_16XFER	0x40		/* 16-bit transfers.		*/

#define	CWC_RSTS	0x00	/* Return status of the specified drive.    */
#define	CWC_RSET	0x01	/* Reset the controller board.		    */
#define	CWC_RDNC	0x02	/* Read with no defective table check.      */
#define	CWC_RDWC	0x03	/* Read with defective table check.         */
#define	CWC_RLNC	0x04	/* Read long with no defective table check. */
#define	CWC_RLWC	0x05	/* Read long with defective table check.    */
#define	CWC_WDNC	0x06	/* Write with no defective table check.     */
#define	CWC_WDWC	0x07	/* Write with defective table check.        */
#define	CWC_WLNC	0x08	/* Write long with no defective table check.*/
#define	CWC_WLWC	0x09	/* Write long with defective table check.   */
#define	CWC_FMT		0x0a	/* Format a track.			    */
#define	CWC_DR1		0x0b	/* Diagnostic read 1.			    */
#define	CWC_DR2		0x0c	/* Diagnostic read 2.			    */
#define	CWC_DW1		0x0d	/* Diagnostic write 1.			    */
#define	CWC_DR3		0x0e	/* Diagnostic read 3.			    */
#define	CWC_DW2		0x0f	/* Diagnostic write 2.			    */
#define	CWC_SEEK	0x10	/* Seek.				    */
#define	CWC_LDCP	0x11	/* Load disk configuration parameters.	    */
#define	CWC_REST	0x12	/* Restore.				    */


typedef struct                  /* THE READ I/O PORTS.                  */
        {                       /* ==================================== */
	U8	cwc_r1;		/* 01: Reserved.			*/
	U8	cwc_rsts;	/* 00: Busy and interrupt status.       */
	U8	cwc_r3;		/* 03: Reserved.			*/
	U8	cwc_r2;		/* 02: Reserved.			*/
	U8	cwc_r5;		/* 05: Reserved.			*/
	U8	cwc_r4;		/* 04: Reserved.			*/
	U8	cwc_r7;		/* 07: Reserved.			*/
	U8	cwc_r6;		/* 06: Reserved.			*/
        }       CWC_R;          /* ==================================== */

typedef struct                  /* THE WRITE I/O PORTS.                 */
        {                       /* ==================================== */
	U8	cwc_w1;		/* 01: Reserved.			*/
	U8	cwc_cpbl;	/* 00: CPB address low byte.		*/
	U8	cwc_w3;		/* 03: Reserved.			*/
	U8	cwc_cpbm;	/* 02: CPB address middle byte.         */
	U8	cwc_w5;		/* 05: Reserved.			*/
	U8	cwc_cpbh;	/* 04: CPB address high byte.           */
	U8	cwc_wrst;	/* 07: Reset.   			*/
	U8	cwc_wgo;	/* 06: Go.       			*/
        }       CWC_W;          /* ==================================== */

typedef union                   /* THE READ AND WRITE I/O PORTS.        */
        {                       /* ==================================== */
        CWC_R   cwc_r;          /* The READ I/O ports.                  */
        CWC_W   cwc_w;          /* The WRITE I/O ports.                 */
        }       CWC;            /* ==================================== */

typedef struct                  /* THE COMMAND PARAMETER BLOCK.         */
        {                       /* ==================================== */
	U8	cwc_unit;	/* 01-01: Unit (drive) number.		*/
	U8	cwc_cmd;	/* 00-00: Command byte code.		*/
	U8	cwc_head;	/* 03-03: Head number.			*/
	U8	cwc_sect;	/* 02-02: Sector number.		*/
	U16	cwc_cyl;	/* 04-05: Cylinder number.		*/
	U32	cwc_bcnt;	/* 06-09: Byte count.			*/
	U32     cwc_addr;	/* 0A-0D: Multibus address.		*/
	U32	cwc_sbad;	/* 0E-11: Status block address.		*/
        }       CWC_CPB;        /* ==================================== */

typedef	struct			/* THE CWC DISK DRIVE PARAMETERS.	*/
	{			/* ==================================== */
	U16	cwc_dbps;	/* 00-01: Bytes per sector.		*/
	U8	cwc_dhpc;	/* 03-03: Heads per cylinder (or drive).*/
	U8	cwc_dspt;	/* 02-02: Sectors per track.		*/
	U16	cwc_dcpd;	/* 04-05: Cylinders per disk.		*/
	U16	cwc_dcrw;	/* 06-07: Cyl. to reduce write current. */
	U16	cwc_dcwp;	/* 08-09: Cyl. to wrire precompensation.*/
	U8	cwc_drsr;	/* 0B-0B: Restore step rate.		*/
	U8	cwc_dnsr;	/* 0A-0A: Normal step rate.		*/
	}	CWC_DDP;	/* ==================================== */

typedef	struct			/* THE CWC STATUS BLOCK.	        */
	{			/* ==================================== */
	U8	cwc_sbcc;	/* 01-01: Configuration code.		*/
	U8	cwc_sbcs;	/* 00-00: Current status.		*/
	U8	cwc_sbpu;	/* 03-03: Previous unit (drive) number. */
	U8	cwc_sbpc;	/* 02-02: Previous command.		*/
	U8	cwc_sbph;	/* 05-05: Previous head.		*/
	U8	cwc_sbps;	/* 04-04: Previous sector number.	*/
	U16	cwc_sbpy;	/* 06-07: Previous cylinder number.	*/
	U32	cwc_sbrb;	/* 08-0B: Remaining byte count.		*/
	U32	cwc_sbna;	/* 0C-0F: Next DMA address.		*/
	U32	cwc_sbme;	/* 10-13: Ptr. to status block itself.	*/
	U8	cwc_sbe1;	/* 15-15: ECC syndrome/remainder byte 1.*/
	U8	cwc_sbe0;	/* 14-14: ECC syndrome/remainder byte 0.*/
	U8	cwc_sbe3;	/* 17-17: ECC syndrome/remainder byte 3.*/
	U8	cwc_sbe2;	/* 16-16: ECC syndrome/remainder byte 2.*/
	}	CWC_STB;	/* ==================================== */

#ifdef CD68K
char			cwcdbuf[4096];
CWC_CPB		cpbbuf;
CWC_STB		stbbuf;
#endif
static int cerr;

static	U8	cwczero = 0;	/* A byte that is always zero.		*/
	int	cwc1open= 1;	/* Require the actual open?		*/

cwcerror(stb)
	register CWC_STB  *stb;
	{
	cerr = 1;
	printf("\ncwc: error 0x%x on dr:%d cyl:%d trk:%d sec:%d xfr:%d\n",
	       stb->cwc_sbcs,
	       stb->cwc_sbpu,
	       stb->cwc_sbpy,
	       stb->cwc_sbph,
	       stb->cwc_sbps,
	       stb->cwc_sbrb);
	/*
	printf("00-00: Current status ================ %x\n",stb->cwc_sbcs);
	printf("01-01: Configuration code ============ %x\n",stb->cwc_sbcc);
	printf("02-02: Previous command ============== %x\n",stb->cwc_sbpc);
	printf("03-03: Previous unit (drive) number == %x\n",stb->cwc_sbpu);
	printf("04-04: Previous sector number ======== %x\n",stb->cwc_sbps);
	printf("05-05: Previous head ================= %x\n",stb->cwc_sbph);
	printf("06-07: Previous cylinder number ====== %x\n",stb->cwc_sbpy);
	printf("08-0B: Remaining byte count ========== %x\n",stb->cwc_sbrb);
	printf("0C-0F: Next DMA address ============== %x\n",stb->cwc_sbna);
	printf("10-13: Ptr. to status block itself === %x\n",stb->cwc_sbme);
	printf("14-14: ECC syndrome/remainder byte 0 = %x\n",stb->cwc_sbe0);
	printf("15-15: ECC syndrome/remainder byte 1 = %x\n",stb->cwc_sbe1);
	printf("16-16: ECC syndrome/remainder byte 2 = %x\n",stb->cwc_sbe2);
	printf("17-17: ECC syndrome/remainder byte 3 = %x\n",stb->cwc_sbe3);
	*/
	}

cwcbusytmo(errcheck)
	int	errcheck;
	{
        register  CWC          *ioports;
        register  CWC_CPB      *cpb;
	register  CWC_STB      *stb;
        register  struct buf   *bufptr;
	register  int           loop;
	register  int           loopmax;
	register  U8            status;

	ioports = (CWC *)CWCCMDP_VA;
        for (loop = 0, loopmax = CWC_TIMEOUT;  loop < loopmax;  loop++)
            {
            if ((ioports->cwc_r.cwc_rsts & CWC_BUSY) == 0)
                {
                break;
                }
            }
        if (loop == loopmax)
            {
            printf("CWC: BUSY TIMEOUT OF %d, CONTINUING.\n",loop);
            }
	if (errcheck)
	    {
            cpb = (CWC_CPB *)CWCCPBP_VA;
            stb = (CWC_STB *)CWCSTBP_VA;
	    ioports->cwc_w.cwc_wrst = cwczero;	/* Turn interrupt OFF.  */
	    status = stb->cwc_sbcs & 0xf0;
            if (status != 0x80 && status != 0xa0 && status != 0xff && status)
                {
	        cwcerror(CWCSTBP_VA);
		return (1);
                }
	    }
	    return (0);
        }

cwcopen(diob)
        register DIOB  *diob;
        {
        register  CWC      *ioports;
	register  CWC_DDP  *ddp;
        register  int       initloop;
	register  int       drive;

#ifdef	DEBUG
	printf("open: nbpt=%d ntpc=%d ncpd=%d\n",
	diob->diob_dbt,diob->diob_dtc,diob->diob_dcd);
#endif
	if (cwc1open)
	    {
            ioports = (CWC *)CWCCMDP_VA;
            cwccmd(0,CWC_RSET,vtop(CWCDBUF_VA),0,0,0,0);
	    sleep(1);
	    for (drive = 0;  drive < 4;  drive++)
	        {
	        cwcbusytmo(0);
	        ddp = (CWC_DDP *)CWCDBUF_VA;
	        ddp->cwc_dbps = 512;
	        ddp->cwc_dhpc = diob->diob_dtc;
	        ddp->cwc_dspt = diob->diob_dbt;
	        ddp->cwc_dcpd = 1023;
	        ddp->cwc_dcrw = 240;
	        ddp->cwc_dcwp = 240;
	        ddp->cwc_drsr = 0x9e;
	        ddp->cwc_dnsr = 0x0c;
	        cwccmd(drive,CWC_LDCP,vtop(CWCDBUF_VA),0,0,0,sizeof(CWC_DDP));
   	        }
	    cwcbusytmo(0);
	    cwc1open = 0;
	    }
	}

cwccmd(drive,command,address,cylinder,head,sector,bytecnt)
        U32     drive;
        U32     command;
        U32     address;
        U32     cylinder;
        U32     head;
        U32     sector;
        U32     bytecnt;
        {
        register  CWC       *ioports;
        register  CWC_CPB   *cpb;
	register  CWC_STB   *stb;
	register  U8        *u8ptr;
	register  int       i;
	register  U32       cpb_pa;

#ifdef	DEBUG
	printf("cmd1: dr=%d cmd=%x adr=%x cyl=%d hd=%d sect=%d bytecnt=%d\n",
	       drive,command,address,cylinder,head,sector,bytecnt);
#endif
        ioports = (CWC *)CWCCMDP_VA;
        cpb     = (CWC_CPB *)CWCCPBP_VA;
	stb     = (CWC_STB *)CWCSTBP_VA;
	cwcbusytmo(0);
	cpb->cwc_cmd  = command;
	cpb->cwc_unit = drive;
	cpb->cwc_cyl  = cylinder;
	cpb->cwc_head = head;
	cpb->cwc_sect = sector;
	cpb->cwc_addr = vtop(CWCDBUF_VA);
	cpb->cwc_bcnt = bytecnt;
	cpb->cwc_sbad = vtop(stb);
	for (i = 0, u8ptr = (U8 *)stb;  i < sizeof(CWC_STB);  i++)
	    {
	    *u8ptr = 0xff;
	    }
	cpb_pa = vtop(cpb);
#ifdef	DEBUG
	printf("cmd2: sbad=%x cpb=%x\n",cpb->cwc_sbad,cpb_pa);
#endif
	iocheck (CWCLITES_VA, 1 << drive);
	ioports->cwc_w.cwc_cpbl = (cpb_pa       & 0xff);
	ioports->cwc_w.cwc_cpbm = (cpb_pa >> 8  & 0xff);
	ioports->cwc_w.cwc_cpbh = (cpb_pa >> 16 & 0xff);
	ioports->cwc_w.cwc_wgo  = CWC_16XFER;
	cwcbusytmo(1);
	iocheck(CWCLITES_VA, 0);
	if (command == CWC_RDWC || command == CWC_RDNC)
	    {
	    bcopy(CWCDBUF_VA,address,bytecnt);
	    }
        }

cwcio(diob)
	register DIOB  *diob;
        {
	register int cylinder;
	register int track;
	register int sector;
	register int nbpt;
	register int nbpc;
	register int ablock;
	register int steprate;
	register int drive;
	register int seccnt;
	register U8 *address;

#ifdef	DEBUG
	printf("cwcio: desired block = %d\n",diob->diob_fdb);
#endif
	nbpt     = diob->diob_dbt;
	nbpc     = nbpt * diob->diob_dtc;
	ablock   = diob->diob_fsb + diob->diob_fdb;
        cylinder = ablock / nbpc;
        track    = ablock % nbpc / nbpt;
        sector   = ablock % nbpt;
	drive    = diob->diob_drv;
	steprate = diob->diob_dp1;
	address  = diob->diob_buf;
	seccnt   = diob->diob_nblks;
	cerr = 0;
	cwcopen(diob);
	cwccmd(drive,CWC_RDWC,address,cylinder,track,sector,seccnt<<9);
#ifdef	DEBUG
	printf("cwcio: i/o done\n");
#endif
	return (0);	/* make this return(cerr) if error-checking wanted */
	}

cwcship(drive,cylinder)
	int	drive;
	int	cylinder;
	{
        DIOB    diob;

	diob.diob_dtc = 8;
	diob.diob_dbt = 17;
	cwcopen(&diob);
        cwccmd(drive,CWC_SEEK,0,cylinder,0,0,0);
	}
