#define DEBUG
/*
	@(#)cfc.c	1.11	(2/20/85 09:54:01)
*/
#include "standalone.h"
#include "sys/config.h"

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

#define CFCCMDP_VA	(MBIOBASE + 0x0100)	/* I/O ports base address.   */
#ifdef CD68K
#define CFCDBUF_VA	(localbuf)	  	/* Multibus buffer address.  */
#endif
#ifdef PM68K
#define CFCDBUF_VA	(MBUSMEMS + 0x0400)  	/* Multibus buffer address.  */
#endif

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

#define	DELAY15(p)	{int i;i=(p);while(i--);}

#define	FDC_5INCH	0x08	/* 5.25 inch floppy drive.		*/
#define	FDC_BUSY	0x10	/* Floppy controller chip is busy.	*/
#define	FDC_DBSY	0x0f	/* Drives busy.				*/
#define	FDC_DDEN	0x40	/* Double density (MFM).		*/
#define	FDC_DIO		0x40	/* Data I/O.				*/
#define	FDC_FORMAT	0x4d	/* Command sequence opcode: format.	*/
#define	FDC_MTON	0x04	/* Motor on.				*/
#define	FDC_READ	0x06	/* Command sequence opcode: read.	*/
#define	FDC_RECA	0x07	/* Command sequence opcode: recalibrate.*/
#define	FDC_RQM		0x80	/* Request for master.			*/
#define	FDC_S512	0x02	/* 512 bytes per sector.		*/
#define	FDC_SEEK	0x0f	/* Command sequence opcode: seek.	*/
#define	FDC_SIS		0x08	/* Sense interrupt status.		*/
#define	FDC_SPEC	0x03	/* Command sequence opcode: specify.	*/
#define	FDC_WRITE	0x05	/* Command sequence opcode: write.	*/

#define	DMA_DOFF	0x04	/* DMA disable.				*/
#define	DMA_READ	0x44	/* DMA setup to read.			*/
#define	DMA_WRITE	0x48	/* DMA setup to write.			*/

/*
	New floppy board - differences only
*/
#define NEW_DMA_SETUP	0x40	/* new setup DMA control	*/
#define OLD_DMA_SETUP	(zero)	/* old setup DMA control	*/

#define NEW_MOT_STS	0x40	/* new motor status		*/
#define NEW_ON_MOT	0x87	/* new turn on the motor	*/
#define NEW_OFF_MOT	0x07	/* new turn off the motor	*/
#define OLD_MOT_STS	0x04	/* old motor status		*/
#define OLD_ON_MOT	0x0C	/* old turn on the motor	*/
#define OLD_OFF_MOT	0x08	/* old turn off the motor	*/

#define NEW_FINTRQ	0x80	/* new floppy interrupt		*/
#define OLD_FINTRQ	0x01	/* old floppy interrupt		*/

typedef struct                  /* THE READ I/O PORTS.                  */
        {                       /* ==================================== */
	U8	cfc_bcnt;	/* 0x01: DMA byte count.		*/
	U8	cfc_doff;	/* 0x00: DMA offset base address.	*/
	U8	cfc_rs03;	/* 0x03: RESERVED, DO NOT USE.		*/
	U8	cfc_rs02;	/* 0x02: RESERVED, DO NOT USE.		*/
	U8	cfc_rs05;	/* 0x05: RESERVED, DO NOT USE.		*/
	U8	cfc_rs04;	/* 0x04: RESERVED, DO NOT USE.		*/
	U8	cfc_rs07;	/* 0x07: RESERVED, DO NOT USE.		*/
	U8	cfc_rs06;	/* 0x06: RESERVED, DO NOT USE.		*/
	U8	cfc_rs09;	/* 0x09: RESERVED, DO NOT USE.		*/
	U8	cfc_stat;	/* 0x08: DMA status register.		*/
	U8	cfc_rs0b;	/* 0x0b: RESERVED, DO NOT USE.		*/
	U8	cfc_rs0a;	/* 0x0a: RESERVED, DO NOT USE.		*/
	U8	cfc_rs0d;	/* 0x0d: RESERVED, DO NOT USE.		*/
	U8	cfc_rs0c;	/* 0x0c: RESERVED, DO NOT USE.		*/
	U8	cfc_rs0f;	/* 0x0f: RESERVED, DO NOT USE.		*/
	U8	cfc_rs0e;	/* 0x0e: RESERVED, DO NOT USE.		*/
	U8	cfc_data;	/* 0x11: FDC data register.		*/
	U8	cfc_msts;	/* 0x10: FDC main status register.	*/
	U8	cfc_rs13;	/* 0x13: RESERVED, DO NOT USE.		*/
	U8	cfc_imst;	/* 0x12: Interrupt & motor status.	*/
	U8	cfc_rs15;	/* 0x15: RESERVED, DO NOT USE.		*/
	U8	cfc_rs14;	/* 0x14: RESERVED, DO NOT USE.		*/
	U8	cfc_rs17;	/* 0x17: RESERVED, DO NOT USE.		*/
	U8	cfc_tsts;	/* 0x16: Tape command/status register.  */
        }       CFC_R;          /* ==================================== */

typedef struct                  /* THE WRITE I/O PORTS.                 */
        {                       /* ==================================== */
	U8	cfc_dcnt;	/* 0x01: DMA byte count.		*/
	U8	cfc_dcar;	/* 0x00: DMA current address register.	*/
	U8	cfc_rs03;	/* 0x03: RESERVED, DO NOT USE.		*/
	U8	cfc_rs02;	/* 0x02: RESERVED, DO NOT USE.		*/
	U8	cfc_rs05;	/* 0x05: RESERVED, DO NOT USE.		*/
	U8	cfc_rs04;	/* 0x04: RESERVED, DO NOT USE.		*/
	U8	cfc_rs07;	/* 0x07: RESERVED, DO NOT USE.		*/
	U8	cfc_rs06;	/* 0x06: RESERVED, DO NOT USE.		*/
	U8	cfc_reqr;	/* 0x09: DMA request register.		*/
	U8	cfc_cmdr;	/* 0x08: DMA command register.		*/
	U8	cfc_mode;	/* 0x0b: DMA mode register.		*/
	U8	cfc_mask;	/* 0x0a: DMA single mask register.	*/
	U8	cfc_ctlr;	/* 0x0d: DMA controller reset.		*/
	U8	cfc_bptr;	/* 0x0c: DMA byte pointer reset.	*/
	U8	cfc_fmsk;	/* 0x0f: DMA full mask register.	*/
	U8	cfc_rs0e;	/* 0x0e: RESERVED, DO NOT USE.		*/
	U8	cfc_data;	/* 0x11: FDC data register.		*/
	U8	cfc_msts;	/* 0x10: FDC main status register.	*/
	U8	cfc_mcrs;	/* 0x13: Master controller reset.	*/
	U8	cfc_mmcr;	/* 0x12: Mode & motor control register. */
	U8	cfc_sahb;	/* 0x15: DMA segment address, high byte.*/
	U8	cfc_salb;	/* 0x14: DMA segment address, low byte. */
	U8	cfc_rx17;	/* 0x17: RESERVED, DO NOT USE.		*/
	U8	cfc_tcmd;	/* 0x16: Tape command/status register.  */
        }       CFC_W;          /* ==================================== */

typedef union                   /* THE READ AND WRITE I/O PORTS.        */
        {                       /* ==================================== */
        CFC_R   cfc_r;          /* The READ I/O ports.                  */
        CFC_W   cfc_w;          /* The WRITE I/O ports.                 */
        }       CFC;            /* ==================================== */

				/* The "where is" the arm cylinder no.	*/
static	int	wherearm[4]  =  {255, 255, 255, 255};
static	int	wheredrv;	/* Drive upon which the seek is active.	*/
static	int	wherepnd;	/* Cylinder to which we are seeking.	*/

static	U8	seq_spec[3]  =  {FDC_SPEC,0xef,0x50};
static	U8	seq_seek[3]  =  {FDC_SEEK,0,0};
static  U8      seq_read[9]  =  {0,0,0,0,0,0,0,0,0};
static	U8	seq_reca[2]  =  {FDC_RECA,0};
static  U8      seq_write[9] =  {0,0,0,0,0,0,0,0,0};
static	U8	seq_format[6]=  {0,0,0,0,0,0};

U8		cfcstatus[16];	/* Status byte table.			*/
static  int     stsindex;	/* Status byte inder.			*/
#ifdef CD68K
static	U8     *localbuf;	/* Pointer to buffer on the stack.	*/
#endif
static	U8	zero = 0;	/* A zero byte.				*/

static	short	new_board = 0;	/* Nonzero if the new controller board	*/

cfcinit()
	{
        register  CFC  *ioports;

	if (iocheck (&((CFC_R *) CFCCMDP_VA)->cfc_tsts, -1) >= 0)
		new_board = 1;		/* must be new */
	else
		new_board = 0;		/* guess it's old */
#ifdef DEBUG
	printf ("cfc:  board type %d\n", new_board);
#endif
        ioports = (CFC *)CFCCMDP_VA;
	ioports->cfc_w.cfc_mcrs = zero;
	DELAY15(100);
	ioports->cfc_w.cfc_mmcr = new_board ? NEW_OFF_MOT : OLD_OFF_MOT;
	if (! new_board) ioports->cfc_w.cfc_salb = zero;
	ioports->cfc_w.cfc_sahb = zero;
	ioports->cfc_w.cfc_ctlr = zero;
	ioports->cfc_w.cfc_cmdr = new_board ? NEW_DMA_SETUP : OLD_DMA_SETUP;
	ioports->cfc_w.cfc_mode = zero;
	ioports->cfc_w.cfc_mode = 1;
	ioports->cfc_w.cfc_mode = 2;
	ioports->cfc_w.cfc_mode = 3;
	cfcscmd(seq_spec,sizeof(seq_spec));
	DELAY15(100);
	}

cfcrecal(drive)
	U32	drive;
	{

	wheredrv = drive;
	wherepnd = 0;
	seq_reca[1] = drive & 0x03;
	cfcscmd(seq_reca,sizeof(seq_reca));
	}

cfccmd (drive, address, cylinder, head, sector, sectrcnt)
        U32     drive;
        U8     *address;
        U32     cylinder;
        U32     head;
        U32     sector;
        U32     sectrcnt;
        {
        register  CFC       *ioports;
	static    int        firsttime = 1;

#ifdef DEBUG
printf("cfccmd: cy=%d hd=%d sc=%d\n", cylinder,head,sector);
#endif DEBUG
	if (firsttime)
	    {
	    cfcinit();
	    firsttime = 0;
	    }
        ioports = (CFC *)CFCCMDP_VA;
	if (cylinder != wherearm[drive])
	    cfcxseek (drive, cylinder, head);
        cfcxread (drive, cylinder, head, sector, sectrcnt, address);
        }

cfcxseek(drive,cylinder,track)
	U32	drive;
	U32	cylinder;
	U32	track;
	{

#ifdef DEBUG
	printf("\rCFC: xseek: dr:%d cy:%d tr:%d\n",drive,cylinder,track);
#endif
	if (wherearm[drive] == cylinder)
	    {
	    printf("FLOPPY: INTERNAL ERROR 1, CONTINUING.\n");
	    }
	seq_seek[1] = (track == 1 ? 4 : 0)  |  (drive & 0x03);
	seq_seek[2] = cylinder;
	if (cylinder == 0)
	    {
	    cfcrecal(drive);
	    }
	else
	    {
	    wheredrv = drive;
	    wherepnd = cylinder;
	    cfcscmd(seq_seek,sizeof(seq_seek));
	    }
	}

cfcxread(drive,cylinder,track,sector,sectrcnt,address)
	U32	drive;
	U32	cylinder;
	U32	track;
	U32	sector;
	U32	sectrcnt;
	U32     address;
	{
	register CFC  *ioports;
	register U32   paddr;
	register U32   count;
	register U32   imst1;
	register U32   imst2;


#ifdef DEBUG
	printf("\rCFC: xread: dr:%d cy:%d tr:%d se:%d sc:%d ad:%x\n",
               drive,cylinder,track,sector,sectrcnt,address);
#endif

	ioports = (CFC *)CFCCMDP_VA;
	ioports->cfc_w.cfc_mmcr = new_board ? NEW_OFF_MOT : OLD_OFF_MOT;
	imst1 = ioports->cfc_r.cfc_imst;
	imst2 = ioports->cfc_r.cfc_imst;
	ioports->cfc_w.cfc_mmcr = new_board ? NEW_ON_MOT : OLD_ON_MOT;
	if (new_board)
	    {
	    imst1 &= NEW_MOT_STS;
	    imst2 &= NEW_MOT_STS;
	    }
	else
	    {
	    imst1 &= OLD_MOT_STS;
	    imst2 &= OLD_MOT_STS;
	    }
	if (imst1 == 0  &&  imst2 == 0)
	    {
	    DELAY15(20000);
	    }
	paddr = vtop(CFCDBUF_VA);
	count = (sectrcnt << 9) - 1;
	ioports->cfc_w.cfc_mask = DMA_DOFF;	/* Be sure DMA disabled */
	ioports->cfc_w.cfc_bptr = zero;		/* Clear first/last FF	*/
	if (new_board)
		{
		ioports->cfc_w.cfc_dcar = paddr;      /* LSB of offset addr */
		ioports->cfc_w.cfc_dcar = paddr >> 8; /* MSB of offset addr  */
		ioports->cfc_w.cfc_salb = paddr >>16; /* XSB of segment addr */
		}
	else
		{
		ioports->cfc_w.cfc_dcar = paddr & 0x0F;	/* LSB of offset adr */
		ioports->cfc_w.cfc_dcar = zero;       /* MSB of offset addr  */
		ioports->cfc_w.cfc_salb = paddr >> 4; /* LSB of segment addr */
		ioports->cfc_w.cfc_sahb = paddr >>12; /* MSB of segment addr */
		}
	ioports->cfc_w.cfc_dcnt = count;	/* LSB of DMA count     */
	ioports->cfc_w.cfc_dcnt = count >> 8;	/* MSB of DMA count     */
	ioports->cfc_w.cfc_mode = DMA_READ;	/* Read to host         */
	ioports->cfc_w.cfc_mask = zero;		/* Let the DMA go ..... */
	seq_read[0] = FDC_READ | FDC_DDEN;
	seq_read[1] = (track == 1 ? 4 : 0) | drive & 0x03;
	seq_read[2] = cylinder;
	seq_read[3] = (track == 1 ? 1 : 0);
	seq_read[4] = sector;
	seq_read[5] = FDC_S512;
	seq_read[6] = 8;	/* 512 byte sectors */
	seq_read[7] = 0x2a;	/* gap length */
	seq_read[8] = 0xff;	/* for 512 bytes sectors */
	cfcscmd(seq_read,sizeof(seq_read));
	bcopy(CFCDBUF_VA,address,sectrcnt<<9);
	}

cfcscmd(sequence,length)
	register U8    *sequence;
	register int    length;
	{
	register CFC   *ioports;
	register int    busyloop;
	register U32    msts;
	register int	i;

        ioports = (CFC *)CFCCMDP_VA;
        DELAY15 (4);
	busyloop = 300;
	while (busyloop--)
	    {
	    msts = ioports->cfc_r.cfc_msts;
	    if (!(msts & (FDC_DBSY | FDC_BUSY)))
		{
		break;
		}
	    }
	if (busyloop == 0)
	    {
	    printf("FLOPPY: TIMEOUT ERROR 1, CONTINUING.\n");
	    }
	i = 1;
	while (length--)
	    {
	    busyloop = 512;
	    while (busyloop--)
		{
		msts = ioports->cfc_r.cfc_msts;
		if ((msts & (FDC_RQM | FDC_DIO)) == FDC_RQM)
		    {
		    break;
		    }
		}
	    if (busyloop == 0)
		{
		printf("FLOPPY: TIMEOUT ERROR 2, CONTINUING.\n");
		}
	    ioports->cfc_w.cfc_data = *sequence++;
	    DELAY15(2);
	    }
	cfcdone();
	}

cfcdone()
        {
        register  CFC          *ioports;
        register  struct buf   *bufptr;
	register  int		busyloop;
	register  int		index;
        register  unsigned int  msts;
        register  unsigned int  imst;
	U8			dmab0,dmab1,dmab2,dmab3;
	static    int           firstini = 1;
	static    int		firstint = 1;
	static	  int           errcount = 0;

	ioports = (CFC *)CFCCMDP_VA;
	busyloop = 500000;
	DELAY15(10);
	while (!((imst = ioports->cfc_r.cfc_imst)
	   & (new_board ? NEW_FINTRQ : OLD_FINTRQ)))
	    {
	    if (busyloop == 0)
		{
		break;
		}
	    else
		{
		busyloop--;
		}
	    DELAY15(3);
	    }
	if (busyloop == 0)
	    printf ("FLOPPY: TIMEOUT ERROR 4, (%x), CONTINUING.\n", imst);
	ioports->cfc_w.cfc_bptr = zero;
	dmab0 = ioports->cfc_w.cfc_dcar;
	dmab1 = ioports->cfc_w.cfc_dcar;
	dmab2 = ioports->cfc_w.cfc_dcnt;
	dmab3 = ioports->cfc_w.cfc_dcnt;
/*
	printf("\rCFC: Entry to interrupt handler.\n");
	printf("DMA: ca: %x %x   wc: %x %x\n",dmab0,dmab1,dmab2,dmab3);
	DELAY15(100000);
*/
	msts = ioports->cfc_r.cfc_msts;
	if (!(msts & FDC_BUSY))
	    {
	    ioports->cfc_w.cfc_data = FDC_SIS;
	    }
	for (stsindex = 0;  stsindex < sizeof(cfcstatus);  stsindex++)
	    {
	    for (busyloop = 0;  busyloop < 6000;  busyloop++)
		{
	        msts = ioports->cfc_r.cfc_msts;
		if ((msts & (FDC_RQM | FDC_DIO)) ==  (FDC_RQM | FDC_DIO))
		    {
		    break;
		    }
		}
	    if ((msts & (FDC_RQM | FDC_DIO))  !=  (FDC_RQM | FDC_DIO))
		{
		printf("FLOPPY: TIMEOUT ERROR 3, (%x), CONTINUING.\n",msts);
		return;
		}
	    cfcstatus[stsindex] = ioports->cfc_r.cfc_data;
	    DELAY15(2);                 /* Delay 2 * 15 microseconds.  */
	    msts = ioports->cfc_r.cfc_msts;
	    if (!(msts & FDC_BUSY))
		{
		break;
		}
	    }
        if (cfcstatus[0] & 0xc0)  	/* I/O result was NOT O.K.          */
            {
	    if (++errcount > 1)
		{
	        printf("CFC ERROR: ");
	        for (index = 0;  index < stsindex;  index++)
		    {
		    printf ("ST%d=%x ", index, cfcstatus[index]);
		    }
	        putchar('\n');
		}
	    }
	}

cfcio(diob)
	register DIOB  *diob;
        {
	register  int	cylinder;
	register  int	track;
	register  int	sector;
	register  int   nbpt;
	register  int	nbpc;
	register  int	ablock;
	register  int   drive;
	register  int   seccnt;
	register  U8   *address;
#ifdef CD68K
	U8              lbuf[4096];
#endif
	static	  int   count = 1;

#ifdef CD68K
	localbuf = lbuf;
#endif

#ifdef	DEBUG
	printf("\n\nENTRY=%d\ncfcio: desired block = %d\n",
               count++,diob->diob_fdb);
#ifdef CD68K
	printf("cfcio: local buffer = %x\n",localbuf);
#endif
#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 + 1;
	drive    = diob->diob_drv;
	address  = diob->diob_buf;
	seccnt   = diob->diob_nblks;
	cfccmd(drive, address, cylinder, track, sector, seccnt);
#ifdef	DEBUG
	printf("cfcio: i/o done\n");
#endif
	return (0);
	}
