/*
 * Megatek Whizzard 7200 PE Parallel Interface Driver
 *
 * Joel Carter/Dan Ladermann (TWG)
 */

#ifdef  SCCS_ID
static char SCCS_ID [] = "%Z%%M%        %I%     %Y% %U% - %E% ";
#endif  SCCS_ID



#include "../h/param.h"
#include "../h/buf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../new/xselch.h"
#include "../h/meg.h"
#undef trace
/* Various manifests */
#define MAXMG   2               /* leave it at 2 for now */
#define MGTRACE (01<<31)

/* Status bit definitions */

#define BTO     0x04        /* Bus Timeout- Graphics Bus Interrupt */
#define BUSY    0x08
#define PREQ    0x10    /* Peripheral Request- Peripheral Bus Interrupt */
#define SIN02   0x20
#define IFBT0   0x40
#define IDR     0x80

/* ULI commands */
/* All data transfers with these commands are halfword transfers */

#define RESETB          0x2A
#define STOPREAD        0x29
#define RESET           0x28
#define WRPERDATA       0x27
#define WRPERADDR       0x26
#define WRGRDATA        0x25
#define WRGRADDR        0x24
#define RDPERDATA       0x23
#define RDPERADDR       0x22
#define RDGRDATA        0x21
#define RDGRADD         0x20

/* Condition Codes for Peripheral Address */
/* Peripheral Address is of the form CCPPPPPP
 * where PPPPPP is the address and CC is one
 * of the following:
 */

#define LOADADD         00
#define INTACK          01
#define PERREAD         02
#define PERPULSE        03

/* ULI Interrupt Control definitions */
#define DISABLE 0x80
#define ENABLE  0x40
#define DISARM  0xC0
#define DO      0x01    /* prog i/o */
/* Peripheral Command Flags */
#define P_READ 0
#define P_WRITE 1
/* Peripheral Interrupt Codes */
#define IPCU 0x34
#define FSCD 0x18
#define PICK 0x10
#define RASTER 0x08

/* Vector Memory Reserved Locations */
#define INTMASK 0x0E
typedef int Ulicommand;

int      nmeg = 1;
extern char    periaddr[];

extern int      megxselch;
extern char     megaddr[];
int megstart(), megscintr(), megstrategy();
#define uliaddr megaddr[0]       /* address of the ULI */

struct buf  megbuf;

struct xselchq megscq = {
	&megstart,
	&megscintr,
	0,0,
};

/* State bits for software model */
#define MGISOPEN        0x01

struct megtab {
	char    m_state;
	unsigned short m_data;      /* data returned from device */
	unsigned short m_bto_data[2]; /* data returned from interrupt */
} megtab[MAXMG];

struct buf meghead;             /* header of queue of activation records */

unsigned short graphaddr;       /* address into 64K graphics memory */
/* I assume that an assignment of an off_t into an unsigned short
 * gives me the least significant 16 bits
 */





megopen (dev, flag)
  dev_t    dev;
  int      flag;
{
    register int     min;

    trace(MGTRACE, "megopen", dev);
    min = minor(dev);
    if(0 <= min && min < nmeg)
        if (flag)
	    if (! (megtab[min].m_state & MGISOPEN) ) {
                /* initialize (maybe) */
		megtab[min].m_state = MGISOPEN;
	    } else
                u.u_error = EACCES;
        else
            u.u_error = EPERM;
    else
        u.u_error = ENXIO;
}

megclose(dev)
  dev_t   dev;
{
    trace(MGTRACE, "megclose", dev);
    megtab[minor(dev)].m_state = 0;
}

megread(dev)
  dev_t dev;
{
	trace(MGTRACE,"megread",dev);

	if (minor(dev) == 0)    /* graphics memory */
	physio(megstrategy, &megbuf, dev, B_READ);
	else if (minor(dev) == 1)       /* peripheral bus */
	percommand(dev,P_READ);
}

megwrite(dev)
  dev_t    dev;
{
	trace(MGTRACE, "megwrite", dev);


	if (minor(dev) == 0)    /* graphics memory */
	physio (megstrategy, &megbuf, dev, B_WRITE);
	else if (minor(dev) == 1)       /* peripheral bus */
	percommand(dev,P_WRITE);
}

percommand(dev,cmd)
   dev_t   dev;
{
	short addr;

	xselchreq(megxselch,&megscq,dev);
	addr = periaddr[minor(dev)];
	oc(uliaddr,WRPERADDR);          /* give ULI the peripheral address */

	whilebusy(uliaddr);
	wh(uliaddr,addr);              /* write the peripheral address */
	oc(uliaddr, cmd? RDPERDATA : WRPERDATA);
	whilebusy(uliaddr);
	if (cmd == P_READ)
		megtab[minor(dev)].m_data = rdh(uliaddr);    /* get peripeheral
							   value for user */
	xselchfree(megxselch);
}


megstrategy (bp)
  register struct buf  *bp;
{

    trace(MGTRACE, "megstrat, bytes=", megbuf.b_bcount);
    graphaddr = u.u_offset;             /* get file location from seek */
    xselchreq (megxselch, &megscq,0);    /* 0 for graphics memory */
}

megstart(dev)
  dev_t dev;
{

    trace(MGTRACE, "megstart", dev);
    oc(megxselch,STOP);
    if( minor(dev) != 0 ) {     /* not graphics memory */
	return;
    }
    oc (uliaddr, ENABLE);               /* enable interrupts from ULI */
    trace(MGTRACE, "mgst", ss(uliaddr));
    oc (uliaddr, WRGRADDR);
    trace(MGTRACE, "mgst", ss(uliaddr));

/*  while (ss(uliaddr) == BUSY);        not necessary or wrong because
					interface is waiting for a write */
    wh(uliaddr,graphaddr);
    trace(MGTRACE, "mgst", ss(uliaddr));

    whilebusy(uliaddr);
    trace(MGTRACE, "mgst", ss(uliaddr));


    wdh (megxselch, megbuf.b_un.b_addr);
    wdh (megxselch, megbuf.b_un.b_addr+megbuf.b_bcount-1);
    printf("megbuf.st = %d, megbug.cnt = %d\n",megbuf.b_un.b_addr,megbuf.b_bcount);
    oc (uliaddr,(megbuf.b_flags&B_READ)?(RDGRDATA):(WRGRDATA));
    trace(MGTRACE, "uliprog", (megbuf.b_flags&B_READ));
    trace(MGTRACE, "mgst", ss(uliaddr));

    oc (megxselch, (megbuf.b_flags&B_READ)?READ_GO:GO);
    trace(MGTRACE, "selprog", (megbuf.b_flags&B_READ));

}
 /* this routine processes interrupts that are internally generated by the
    selch */
/* we assume that the selch will always be knocked down when communicating
 * with the Megatek peripherals
 * thus the only way we will get an interrupt from the selch controller
 * is after a graphics memory data transfer
 */
megscintr(selchdev, selchstat, dev)
   dev_t selchdev, dev;
{
    register int     endadd;

    trace(MGTRACE, "megscintr", dev);
    oc (megxselch, STOP);

    if( minor(dev) != 0 ) {             /* not graphics memory */
	return;
    }
    if (megbuf.b_flags & B_READ)
    {
	oc(uliaddr,STOPREAD);
    }

    trace(MGTRACE, "megscintr, stat=", ss(uliaddr));
    endadd = rh (megxselch);
    if (endadd < megbuf.b_un.b_addr+megbuf.b_bcount-1)
	megbuf.b_flags |= B_ERROR;
    else
	megbuf.b_resid = 0;
    trace(MGTRACE, "megscintr1, stat=", ss(uliaddr));
    iodone (&megbuf);
    xselchfree (megxselch);





}
/* this routine processes interrupts from the ULI
 * there are two types of possible interrupts from the ULI
 * one is a peripheral bus interrupt signalled by PREQ in the ULI
 * status register
 * the other is a graphics bus interrupt  signalled by BTO in the ULI
 * status register
 * BTO is automatically reset by the next command to the ULI
 * megintr is guaranteed to have the selch ( otherwise the interrupt )
 * would never have been seen
 * this code will run to completion
 */
megintr(dev)
  int    dev;    /* from devmap[] in c.c */
{
    trace(MGTRACE, "megintr", dev);


/* handle peripheral interrupts */

    if (ss(uliaddr) == PREQ)            /* peripheral request*/
    {
	register int peraddr;

	oc (uliaddr, (INTACK << 6) );         /* acknowledge interrupt */
	while (ss(uliaddr) != IDR);  /* wait for the peripheral's address
					to be latched */
	peraddr = rh(uliaddr);                /* read the address */
	switch (peraddr) {

		case IPCU:
			   oc(uliaddr, PERREAD << 6); /* initiate Peripheral  Read */

			   if ( ss(uliaddr) != IDR) { /* if no data, the
						       * peripheral was
						       * acknowledging a write
						       */
			       oc (uliaddr, PERPULSE << 6);
			       break;
			   }
			   megbuf.m_data = rh(uliaddr); /* get peripheral
							 * data for users
							 * program
							 */
			   break;
		case FSCD:

		case PICK:

		case RASTER:
		default:
			   break;
	}
     }
     else
     if (ss(uliaddr) == BTO)            /* graphics bus interrupt */
     {
    trace(MGTRACE, "megbtointr", 0);

	oc(uliaddr,WRGRADDR);
	wh(uliaddr,INTMASK);
	whilebusy(uliaddr);
	oc(uliaddr, RDGRDATA);
	megbuf.m_bto_data[1] = rh(uliaddr);     /* least sign. 16 bits */
	megbuf.m_bto_data[2] = rh(uliaddr);     /* most sign. 16 bits */
/* A valid read from Vector Memory clears the BTO bit */
/*      megbuf.b_flags |= B_ERROR;      /* return an error to user program
					 * can't use u.u_error
					 */
     }
     else printf("spurious int %d\n",dev);  /* spurious interrupt */
}

megioctl(dev,cmd,addr,flag)
  caddr_t addr;
  dev_t dev;
{
	trace( MGTRACE, "megioctl", dev);
	if (cmd == RESET)
	{
	    xselchreq(megxselch,&megscq,dev);
	    oc(uliaddr,RESET);
	    whilebusy(uliaddr);
	    sleep(2);       /* delay to make sure the IPCU has done its job */
	    xselchfree(megxselch);
	 }
	 else
	 if (cmd == DISABLE)
	 {
	    xselchreq(megxselch,&megscq,dev);
	    oc(uliaddr,DISABLE);
	    whilebusy(uliaddr);
	    xselchfree(megxselch);
	 }
	 else
	 if (cmd == ENABLE)
	 {
	    xselchreq(megxselch,&megscq,dev);
	    oc(uliaddr,ENABLE);
	    whilebusy(uliaddr);
	    xselchfree(megxselch);
	 }
	 else
	 if (cmd == DISARM)
	 {
	    xselchreq(megxselch,&megscq,dev);
	    oc(uliaddr,DISARM);
	    whilebusy(uliaddr);
	    xselchfree(megxselch);
	 }
	 if (cmd == DO)   {
		oc (megxselch, STOP) ;  /* knock down the selch */
		oc (uliaddr, RESET);
		trace (MGTRACE, "reset" , ss(uliaddr));
		sleep(2);
		whilebusy(uliaddr);
		oc (uliaddr, WRGRADDR);
		wh (uliaddr, 1);
		whilebusy(uliaddr);
		oc (uliaddr, WRGRDATA);
		wh (uliaddr, 0);
		whilebusy(uliaddr);
		wh (uliaddr, 0xC000);
		whilebusy(uliaddr);
		oc (uliaddr, WRGRADDR);
		wh (uliaddr, 0xFFF1);
		whilebusy(uliaddr);
		oc (uliaddr, WRGRDATA);
		wh (uliaddr, 0x0057);
		whilebusy(uliaddr);
		wh (uliaddr, 0x0000);
		whilebusy(uliaddr);

	}
}

whilebusy(addr)
int addr;
{
    trace(MGTRACE, "whilebusy addr = ", addr);

    while (ss(addr) == BUSY) ;

}
