/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION  1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:dma.c 12.0$ */
/* $ACIS:dma.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/caio/RCS/dma.c,v $ */

#ifdef IBMRTPC

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:dma.c 12.0$";
#endif

/* #include "saio.h" */

#include "../h/param.h"
#include "../h/vm.h"
#include "../h/buf.h"
#include "../h/time.h"
#include "../h/proc.h"
#include "../h/errno.h"
#include "../machine/pte.h"
#include "../machine/io.h"
#include "../machine/debug.h"
#include "../machineio/ioccvar.h"

#include "../machine/mmu.h"
caddr_t real_buf_addr();

#include "../machineio/dmavar.h"
#include "../machineio/dmareg.h"

/* "local" global variables (global used by dma.c only) */
/*  header structures per channel */
LOCAL struct iocc_hd iocc_hdr[DMA_MAX_CHAN];
LOCAL unsigned dma_need_int;

#ifdef REGION
LOCAL unsigned tcw_region_groups;	  /* allocated region groups */
LOCAL unsigned tcw_region_map[TCW_GROUPS]; /* tcw allocated within a group */
#endif REGION

#ifdef DEBUG
int	dmadebug=0;
#endif
/*
 * variable to dump hardware statuses (read to generate a hardware
 * side effect).
 */
char 	dma_junk;

/*
 * initialize dma data structures at boot time
 */
dma_init()
{
register struct dma_8bit_device volatile *regp8=(struct dma_8bit_device volatile *) DM_CTL1_BASE;
register struct dma_16bit_device volatile *regp16=(struct dma_16bit_device volatile *) DM_CTL2_BASE;

/*
 * initialize per channel data structure. Take care of 8237 subchannel
 * assignement (weird assignment).
 */
	static short sub_chan[8] = {
		2, 1, 0, 3, 0, 1, 2, 3
	};
	int i;

	DEBUGF(dmadebug, printf("dma_init called\n"););

/* 8 bit */
	for (i = DMA_CHAN0; i <= DMA_CHAN3; i++) {
		iocc_hdr[i].dh_subchan = sub_chan[i];
		iocc_hdr[i].dh_state = 0; /* mark them not busy */
		iocc_hdr[i].dh_8237_reg = (caddr_t) regp8;	/* hardware registers */
		dma_junk = regp8->dm_cmd;
		regp8->dm_smask = DM_CHAN_DISABLE | sub_chan[i];
		regp8->dm_req = sub_chan[i];
	}
	i = IN(CRRB);
	OUT(CRRB,i | DM_CTL1_ENABLE) ; /* arbitrater enable */
	regp8->dm_cmd = DM_CMD_DEFAULT;


/* 16 bit , do not disable channel 4, it's used by the 8bit controller */
	for (i = DMA_CHAN5; i <= DMA_CHAN7; i++) {
		iocc_hdr[i].dh_subchan = sub_chan[i];
		iocc_hdr[i].dh_state = DMA_IDLE; /* mark them not busy */
		iocc_hdr[i].dh_8237_reg = (caddr_t)regp16;	/* hardware registers */
		dma_junk = regp16->dm_cmd;
		regp16->dm_smask = DM_CHAN_DISABLE | sub_chan[i];
		regp16->dm_req = sub_chan[i];
	}
	i = IN(CRRB);
	OUT(CRRB,i | DM_CTL2_ENABLE) ; /* arbitrer enable */
	regp16->dm_cmd = DM_CMD_DEFAULT;

/* now do channel 8 */
	iocc_hdr[DMA_CHAN8].dh_subchan = 0;
	iocc_hdr[DMA_CHAN8].dh_state = DMA_IDLE; /* mark them not busy */
	iocc_hdr[DMA_CHAN8].dh_8237_reg = (caddr_t) DM_CHAN8_REG;
	* (char volatile *) DM_CHAN8_REG = DM_CHAN8_DISABLE;

#ifdef REGION
/*
 * free all the region groups
 * Note: tcw's which correspond to a resident kernel should be marked 
 * as allocated.
 */
	tcw_region_groups = 0;
	for (i=0 ; i < TCW_GROUPS; i++)
		tcw_region_map[i] = 0;
#endif REGION
	/*
	 * Note: for REGION mode we'll need to define an interface to set
	 * bits in the buffer mode register.  We'll also need to save/restore
	 * said bits when channels are released/grabbed.
	 */
	*(char volatile *)DM_BUFFER = '\0'; /* Buffer mode OFF (all channels) */
}


/* 
 * dma_setup queues the controller structer on one of the channels
 * if the channel is not busy, dma_setup starts the transfer.
 * dma_setup also handles hand shaking with the DMA_CANWAIT and
 * DMA_EXCLUSIVE flags
 */
dma_setup(dm)

	register struct iocc_ctlr *dm;

{
	register struct iocc_hd *dhp;
	int s;

	DEBUGF(dmadebug,
		printf("dma_setup : dev=%s%d chan=%d flags=0x%b state=0x%b\n",
		dm->ic_driver->idr_mname,dm->ic_ctlr,dm->ic_dmachannel,
		dm->ic_dmaflags,DMA_FLAGS,(((dm->ic_dmachannel < DMA_CHAN0) ||
		(dm->ic_dmachannel > DMA_CHAN8)) ? DMA_BAD_CHAN : 
		iocc_hdr[dm->ic_dmachannel].dh_state),DMA_STATES);
	);

	/*
	 * Not all transfers are valid for channel 8, and no transfers are valid
	 * for channel 4.
	 */
	if ((dm->ic_dmachannel == DMA_CHAN4) || ((dm->ic_dmachannel == DMA_CHAN8) && 
	  ((dm->ic_dmaflags & DM_CHAN8_MASK) != DM_CHAN8_NEED)))
		panic("invalid use of dma channel");

	/*
	 * Valid channels are 0 to 8 inclusive.
	 */
	if ((dm->ic_dmachannel < DMA_CHAN0) || (dm->ic_dmachannel > DMA_CHAN8))
		panic("invalide dma channel");

	/* now that we've validated the channel we may use it as an index */
	dhp = &iocc_hdr[dm->ic_dmachannel];

	/* check the can't wait flag */
	if (dm->ic_dmaflags & DMA_CANTWAIT) {
		if (dhp->dh_state & DMA_BUSY) {
			return((dhp->dh_state & DMA_EXCLUSIVE_QUEUED) ? 
				DMA_EXCLUSIVE_RET : DMA_BUSY_RET);
		}
	}

	/* only allow one exclusive request at a time */
	s = spl_dma();
	if (dm->ic_dmaflags & DMA_EXCLUSIVE) {
		if (dhp->dh_state & DMA_EXCLUSIVE_QUEUED) {
			splx(s);
			return(DMA_EXCLUSIVE_RET);
		} else {
			dhp->dh_state |= DMA_EXCLUSIVE_QUEUED;
		}
	}

	/* release and exclusive channel if necessary */
	if (dhp->dh_state & DMA_EXCLUSIVE_RUNNING) {
		if (dhp->dh_actf->ic_driver->idr_chanrelse) {
			dhp->dh_state |= DMA_RELEASE_PENDING;
			if ((*dhp->dh_actf->ic_driver->idr_chanrelse)
					(dm->ic_dmachannel)) {
				dhp->dh_state &= ~DMA_RELEASE_PENDING;
				splx(s);
				return(DMA_EXCLUSIVE_RET);
			}
			dhp->dh_state &= ~DMA_RELEASE_PENDING;
		} else {
				splx(s);
				return(DMA_EXCLUSIVE_RET);
		}
	}

	dm->ic_dmaforw = NULL;		  /* queue request */
	if (dhp->dh_actf == NULL)
		dhp->dh_actf = dm;
	else
		dhp->dh_actl->ic_dmaforw = dm;
	dhp->dh_actl = dm;

	/*
	 * let the exclusive device who had this channel requeue
	 * without really starting the channel
	 */
	if (dhp->dh_state & DMA_RELEASE_PENDING) {
		splx(s);
		return(DMA_OK_RET);
	}

	/* if channel  not busy  process */
	if ((dhp->dh_state & DMA_BUSY) == 0) {  
		dhp->dh_state |= DMA_BUSY;
		dma_in_use++;
		splx(s);
		dma_start(dm->ic_dmachannel);
	} else
		splx(s);

	return (DMA_OK_RET);
}

/*
 * This is a utility routine for multi channel dma devices.
 * it selects the first least busy channel it can use and
 * returns it to the caller. It also sorts the array pushing
 * busy channels to the bottom.
 */
short
dma_select_chan(chan_array)
	short	chan_array[];
{
	int	i;
	int	first_open = DMA_MAX_CHAN;
	int	not_exclusive = DMA_MAX_CHAN;
	int	not_running = DMA_MAX_CHAN;

	/* first look for a completely open channel */
	for (i=0 ; i < DMA_MAX_CHAN && chan_array[i] != DMA_END_CHAN; i++) {
		if ((iocc_hdr[chan_array[i]].dh_state & DMA_BUSY) == 0) {
			first_open = i;
			break;
		}
		if ((iocc_hdr[chan_array[i]].dh_state & DMA_EXCLUSIVE_QUEUED)
									 == 0) {
			not_exclusive = i;
		} else
		if ((iocc_hdr[chan_array[i]].dh_state & DMA_EXCLUSIVE_RUNNING)
									 == 0) {
			not_running = i;
		}
	}


	if (first_open != DMA_MAX_CHAN) {
		return(chan_array[first_open]);
	}
	if (not_exclusive != DMA_MAX_CHAN) {
		return(chan_array[not_exclusive]);
	}
	if (not_running != DMA_MAX_CHAN) {
		return(chan_array[not_running]);
	}
	/* ARGGGH.. things are really busy, we need to interrupt someone */
	return(chan_array[0]);
}

/* 
 * dma_start dispatches to the proper dma setup routine
 */
LOCAL
dma_start(channel)
	short channel;
{
	register struct iocc_ctlr *dmp;
	register struct iocc_hd *dhp = &iocc_hdr[channel];
	int s,s2;


	if ((dmp = dhp->dh_actf) == NULL)
		return (0);		  /* no requests are pending */

	DEBUGF(dmadebug,
		printf("dma_start : dev=%s%d chan=%d flags=0x%b state=0x%b\n",
		dmp->ic_driver->idr_mname,dmp->ic_ctlr,dmp->ic_dmachannel,
		dmp->ic_dmaflags,DMA_FLAGS,dhp->dh_state,DMA_STATES);
	);

	/* handle any DMA_EXCLUSIVE special cases */
	if ( dmp->ic_dmaflags & DMA_EXCLUSIVE ) {
		/*
		 * if there are other requests on the queue, let them go first.
		 * Note if there is high contention for this channel, the 
		 * DMA_EXCLUSIVE may never be queued.
		 */
		s = spl_dma();     /* can't be interupted during DMA_EXCLUSIVE setup */
		if (dmp->ic_dmaforw != NULL) {
			dhp->dh_actf = dmp->ic_dmaforw;
			dhp->dh_actl->ic_dmaforw = dmp;
			dhp->dh_actl = dmp;
			dmp->ic_dmaforw = NULL;
			dmp = dhp->dh_actf;
			splx(s);
		} else {
			dhp->dh_state |= DMA_EXCLUSIVE_RUNNING;
		}
	}

	/* handle any  DMA_NOCANINT special cases */
	if (dmp->ic_dmaflags & DMA_CANTINT) {
		s2=spl_dma();
		dma_need_int |= (1 << dmp->ic_dmachannel);
		splx(s2);
	}

	/* find the proper setup routine to call. CASCADE = 1st party = Alt Controller */
	if ((dmp->ic_dmaflags & DM_XFER) == DMA_CASCADE) {
		/* we're using an alternate controller, set cmd register */
		if (DMA_CTLR_1(channel)) {		/* 8 bit */
			((struct dma_8bit_device volatile *)dhp->dh_8237_reg)->dm_mode = 
			  (DMA_CASCADE | dhp->dh_subchan);
		} else if (DMA_CTLR_2(channel)) {	/* 16 bit */
			((struct dma_16bit_device volatile *)dhp->dh_8237_reg)->dm_mode = 
			  (DMA_CASCADE | dhp->dh_subchan);
		}
		/* channel 8 is already set up as an alternate controller */

		/* must be page or region mode now */
		if (dmp->ic_dmaflags & DMA_REGION) {
			dma_set_region(dmp);
		} else {
			dma_set_page(dmp);
		}
	} else {
		/*
		 * We're using system dma, call controller dependent setup routine
		 * system dma may not be used on CHAN8, but this would have been bounced 
		 * by dma_setup.
		 */
		if (DMA_CTLR_1(channel)) {		/* 8 bit */
			dma_set8(dmp);
		} else if (DMA_CTLR_2(channel)) {	/* 16 bit */
			dma_set16(dmp);
		}
	}

	/* if this was an DMA_EXCLUSIVE setup, we can now go off interupt level */
	if (dhp->dh_state & DMA_EXCLUSIVE_RUNNING) {
		splx(s);
	}
	return;
}

/*
 * dma_map maps desired transfer addresses to unused sections
 * of io space.
 */
unsigned
dma_map(chan,bp)
	short chan;
	struct buf *bp;
{
	/* passed virtual addr */
	unsigned vir_addr = (unsigned) bp->b_un.b_addr;
	unsigned displacement = (vir_addr & TCW_PAGEMASK); 
	unsigned len = bp->b_bcount;
	int tcw_start,tcw_end,i;
	short tcw_num;			  /* tcw number */

#ifdef REGION
	int	flags = iocc_hdr[chan].dh_actf->ic_dmaflags;

	if ((flags & DMA_REGION) == 0)	 {
#endif
	/* Page mode */
		char *real_addr;		  /* real translation */
		short prefix;			  /* tcw prefix */
		unsigned *tcw_bitmask = iocc_hdr[chan].dh_pagebitmask;
		unsigned io_hibits;
		unsigned tcw_need_low;
		unsigned tcw_need_high;

		/* Calculate  the number of TCWs needed */
		tcw_num = (((len+displacement) + TCW_PAGESIZE - 1) / TCW_PAGESIZE); 
		DEBUGF(dmadebug,printf("dma_map called, tcw=%x mask=%x %x\n",
		 tcw_num,tcw_bitmask[1],tcw_bitmask[0]););
	
		/* Look for a continguous block */
		/* build the search mask */
		tcw_need_low = DMA_LEFT_MASK(tcw_num);
		tcw_need_high = (tcw_num > DMA_MASK_SIZE ?
			DMA_LEFT_MASK(tcw_num - DMA_MASK_SIZE) :0 );
		/* look for a contiguous hole */
		for (i=0; i <= (TCW_16BIT - tcw_num); i++, 
			DMA_SHIFT_LEFT(tcw_need_high,tcw_need_low)) {
			if ((tcw_need_low & tcw_bitmask[0]) == 0) {
				if ((tcw_need_high & tcw_bitmask[1]) == 0) {
					break;
				}
			}
		}

		/* not enough free tcw's to map request */
		if (i > (TCW_16BIT - tcw_num)) {
			return(DMA_INV_IOADDR);
		}
	
		/* allocate the block */
		tcw_bitmask[0] |= tcw_need_low;
		tcw_bitmask[1] |= tcw_need_high;
		tcw_start = i;
		tcw_end=tcw_num+tcw_start;

		/* set the high 7 bits if cascade mode */	
		io_hibits = ((iocc_hdr[chan].dh_actf->ic_dmaflags & DM_XFER) 
			== DMA_CASCADE ? DM_HI_BITS : 0);

		/* initialize the TCW's */
		for (i = tcw_start; i < tcw_end;  i++,vir_addr += TCW_PAGESIZE) {
			DEBUGF(dmadebug,printf("dma_map,ioaddr=%x addr=%x",
				(i << TCW_PAGESHIFT)|displacement|io_hibits,
				vir_addr););
			real_addr = real_buf_addr(bp, vir_addr);
			DEBUGF(dmadebug,printf(" real addr=%x\n",real_addr););
			prefix = (short)(((int)real_addr) >> TCW_PAGESHIFT);
			TCW(chan,i) = prefix | TCW_RSC_ACC | TCW_REAL_ACC;
		}

		/* return the I/O address */
		return((tcw_start << TCW_PAGESHIFT) | displacement | io_hibits);
#ifdef REGION
	} else  {
	/* Region mode */
		int tcw_groups,tcw_resid,index,tcw_start_group;
		unsigned tcw_need,resid_need,resid_high;
		unsigned chan_region_map[] = iocc_hdr[chan].hd_region_map;

		/* Calculate  the number of TCWs needed */
		tcw_num = ((len + TCW_REGIONSIZE - 1) / TCW_REGIONSIZE); 
		if (displacement + (len % TCW_REGIONSIZE) > TCW_REGIONSIZE)
			tcw_num++;
		DEBUGF(dmadebug,printf("dma_map called (region), tcw=%x \n",tcw_num););

		/* 
		 * For allocation purposes, region mode TCW are split into
		 * 32 groups (TCW_GROUPS) of 32 (TCW_GROUP_SIZE). This allows
		 * all 512 tcw's to be mapped. Note this allocation scheme
		 * leaves lots of holes in allocation, but the expected use
		 * of region mode is for a small number of large transfers
		 * rather than a large number of small transfers.
		 */
		tcw_groups = tcw_num / TCW_GROUP_SIZE;
		tcw_resid = tcw_num % TCW_GROUP_SIZE;

		/* now find the first group wich satisfies the requirement */
		tcw_need = (1 << tcw_groups) - 1;
		resid_need = (1 << tcw_resid) - 1;
		resid_high = ((1 << tcw_resid) - 1) << (TCW_GROUP_SIZE-tcw_resid);
		tcw_start = TCW_GROUPS;
		for (i = 0; i < TCW_GROUPS; i++,tcw_need <<= 1) {
		    if ((tcw_need & tcw_region_groups) == 0) {
			/* can we fit the resid in the right TCW */
			if ((i+tcw_groups < TCW_GROUPS) && 
			 tcw_region_map[(index=i+tcw_groups)] & resid_need) == 0) {
				tcw_start_group = i;
				tcw_start = (index* TCW_GROUP_SIZE)+tcw_resid;
				break;
			/* can we fit the resid in the left TCW */
			} else if ((i > 0) && ((tcw_region_map[(index=i-1)] & resid_high) == 0) {
				resid_need=resid_high;
				tcw_start_group = i;
				tcw_start = i * TCW_GROUP_SIZE;
				break;
			}
		     }
		}

		/* if none found, return an error */
		if (tcw_start == TCW_GROUPS)
			return (DMA_INV_IOADDR);

		/* now mark the tcws allocated and calculate the ioaddress */
		ioaddr=tcw_start << TCW_REGION_SHIFT;
		/* mark the partial group */
		tcw_region_groups |= 1 << index;
		tcw_region_map[index] |= resid_need;
		chan_region_map[index] |= resid_need;
		/* mark the full groups */
		for (i=tcw_start_group ; i < tcw_start_group+tcw_groups ; i++) {
			tcw_region_groups |= 1 << i;
			tcw_region_map[i] = DMA_ALL_BITS;
			chan_region_map[i] = DMA_ALL_BITS;
		}

		/* 
		 * Now we need to start mapping pages into segment E 
		 * or remapping the TCW to 32K contiguous real memory.
		 * The latter requires lots care by the driver.
		 * *** This is the code that needs to be completed to use
		 * region mode dma.. For region mode virtual each page
		 * needs to be remapped to segment E. For region mode
		 * physical, each page is checked to make sure it's
		 * 32K contiguous.
		 */
		for (i = tcw_start; i < tcw_start+tcw_num ; i++) {
			/*
			 * calculate prefix
			 * set TCW: real mode TCW = prefix,
			 *         virtual mode TCW = i | TCW_VIRTUAL
			 */
			for (j=0; j < TCW_PAGE_REGION ; j++) {
			/*
			 * for real mode: get real address. if = to
			 *  prefix+1 increment prefix else return
			 *  DMA_INV_IOADDR.
			 * for virual mode: get pte. mapout page
			 *  mapin page to address TCW virtual address.
			 */
			}
		}
		/*
		 * we're ready to go.
		 */
		return(ioaddr);
	}
#endif
}

/*
 * dma_free frees mapped io space to allow it to be mapped on the
 * next dma_map call.
 */
int
dma_free_map(chan,ioaddr,len)
	short	chan;
	unsigned ioaddr;
	unsigned len;
{
	int	tcw_start;
	short	tcw_num;

#ifdef REGION
	int	flags = iocc_hdr[chan].dh_actf->ic_dmaflags;

	if ((flags & DMA_REGION) == 0) {
#endif
	/* Page mode */
		unsigned *tcw_pagemask = iocc_hdr[chan].dh_pagebitmask;
		unsigned tcw_free_high,tcw_free_low;
	
		/* Calculate  the number of TCWs to be freed */
		tcw_num = (((ioaddr & TCW_PAGEMASK)+len + TCW_PAGESIZE - 1)
							 / TCW_PAGESIZE); 
		DEBUGF(dmadebug,printf("dma_free_map called, tcw=%x mask=%x %x\n",
		 tcw_num,tcw_pagemask[1],tcw_pagemask[0]););
	
		/* The starting TCW */
		tcw_start = (ioaddr >> TCW_PAGESHIFT) % TCW_16BIT;

		/* set up the compare masks */	
		tcw_free_low = DMA_LEFT_MASK(tcw_num);
		tcw_free_high = (tcw_num > DMA_MASK_SIZE ? 
			    DMA_LEFT_MASK(tcw_num-DMA_MASK_SIZE) : 0);

		/*
		 * Now shift it into place. The following magic performs
		 * a 64 bit shift to the left by tcw_start.
 		 */
		tcw_free_high =  (tcw_free_high << tcw_start) | 
                 (tcw_start > DMA_MASK_SIZE ?
		  tcw_free_low << (tcw_start - DMA_MASK_SIZE) :
		  (tcw_free_low >> (DMA_MASK_SIZE - tcw_start)));

		tcw_free_low = tcw_free_low << tcw_start;

		/* make sure those bits are really allocated */
		if ((tcw_free_low & tcw_pagemask[0]) != tcw_free_low )
			panic("freeing free TCW");
		if ((tcw_free_high & tcw_pagemask[1]) != tcw_free_high )
			panic("freeing free TCW");

		/* free the map */
		tcw_pagemask[0] &= ~tcw_free_low;
		tcw_pagemask[1] &= ~tcw_free_high;
		return(DMA_OK_RET);
#ifdef REGION
	} else {
		unsigned chan_region_map[] = iocc_hdr[chan].hd_region_map;
		unsigned tcw_free;
	
		/* Calculate  the number of TCWs to be freed */
		tcw_num = ((len + TCW_REGIONSIZE - 1) / TCW_REGIONSIZE); 
		if ((ioaddr & TCW_REGIONMASK) > (len % TCW_REGIONSIZE))
			tcw_num++;
	
		tcw_start = (ioaddr >> TCW_REGIONSHIFT);

		for (i = tcw_start; i < tcw_start+tcw_num ; i++) {
			/* deallocate TCW's */
			tcw_region_map[i/TCW_GROUPS]  &= ~(1 << (i%TCW_GROPS);
			chan_region_map[i/TCW_GROUPS]  &= ~(1 << (i%TCW_GROPS);
			if (tcw_region_map[i/TCW_GROUPS] == 0)
				tcw_region_groups &= ~(1 << (i/TCW_GROUPS));
			/* remap pages back in for region mode virtual */
			if (flags & DMA_VIRTUAL) {
				for (j=0; j < TCW_PAGE_REGION ; j++) {
				/*
				 * get pte. mapout page from TCW virtual
				 *  mapin page to original virtual address
				 */
				}
			}
		}
		return(DMA_OK_RET);
	}
#endif
} 

/*
 * Set up the first 8237 controller which corresponds to the 4 8 bit channels.
 */
LOCAL
dma_set8(dm)
	struct iocc_ctlr *dm;
{
	short chan = dm->ic_dmachannel;
	register struct iocc_hd *dhp = &iocc_hdr[chan];
	register struct dma_8bit_device volatile *regp = (struct dma_8bit_device volatile *)dhp->dh_8237_reg;
	register struct buf *bp = dm->ic_dmabuf;

	int ch = dhp->dh_subchan;	  /* 8237 subchannel  */
	int len = bp->b_bcount;
	char mode = dm->ic_dmaflags & DM_MODE_FLAGS;  /* mode byte	 */
	unsigned ioaddr;

	/* setup IOCC registers */
	DMA_SET_PAGE_MODE(chan);

	DEBUGF(dmadebug,
		printf("dma_set8 : dev=%s%d chan=%d flags=0x%b vad=0x%x len=%d\n",
		dm->ic_driver->idr_mname,dm->ic_ctlr,dm->ic_dmachannel,
		dm->ic_dmaflags,DMA_FLAGS,bp->b_un.b_addr,bp->b_bcount)
	);

	/* if the transfer length is 0 there's nothing to xfer */
	if (len <= 0) {
		DEBUGF(dmadebug,printf(" ZERO_LENGTH\n"););
		(*dm->ic_driver->idr_dgo)(dm,0,0,bp);
		return;
	}

	/* we own the channel, but can't address the top TCW's */
	dhp->dh_pagebitmask[0] = 0;
	dhp->dh_pagebitmask[1] = DMA_ALL_BITS;
	/* allocate TCW's */
	if ((ioaddr = dma_map(chan,bp)) == DMA_INV_IOADDR) {
		DEBUGF(dmadebug,printf(" NOT ENOUGH TCW's\n"););
		(*dm->ic_driver->idr_dgo)(dm,0,ioaddr,bp);
		return;
	}

	DEBUGF(dmadebug,printf(" ioaddr=0x%x\n",ioaddr););
	/* set up 8237 controller */
	regp->dm_mode = (mode | ((char)(bp->b_flags & B_READ) ? 
		DM_READ : DM_WRITE) | ch);
	regp->dm_ff = 0;
	regp->dm_chan[ch].dm_base = ioaddr;
	regp->dm_chan[ch].dm_base = ioaddr >> 8;

	regp->dm_ff = 0;
	regp->dm_chan[ch].dm_count = (len-1);
	regp->dm_chan[ch].dm_count = (len-1) >> 8;

	/* call xxdgo */
	(*dm->ic_driver->idr_dgo)(dm,len,ioaddr,bp);
}

/*
 * Set up the second 8237 controller which corresponds to the 3 16 bit channels.
 */
LOCAL
dma_set16(dm)
	struct iocc_ctlr *dm;
{
	short chan = dm->ic_dmachannel;
	struct iocc_hd *dhp = &iocc_hdr[chan];
	struct dma_16bit_device volatile *regp = (struct dma_16bit_device volatile *)dhp->dh_8237_reg;
	struct buf *bp = dm->ic_dmabuf;

	int ch = dhp->dh_subchan;	  /* 8237 subchannel */
	int len = bp->b_bcount;
	char mode = dm->ic_dmaflags & DM_MODE_FLAGS;  /* mode byte	 */
	unsigned ioaddr;


	/* setup IOCC registers	 */
	DMA_SET_PAGE_MODE(chan);

	DEBUGF(dmadebug,
		printf("dma_set16 : dev=%s%d chan=%d flags=0x%b len=%d\n",
		dm->ic_driver->idr_mname,dm->ic_ctlr,dm->ic_dmachannel,
		dm->ic_dmaflags,DMA_FLAGS,len)
	);

	/* if the transfer length is 0 there's nothing to xfer */
	if (len <= 0) {
		(*dm->ic_driver->idr_dgo)(dm,0,0,bp);
		return;
	}

	/* we own the channel, so all TCW's are fair game */
	dhp->dh_pagebitmask[0] = 0;
	dhp->dh_pagebitmask[1] = 0;
	/* allocate the TCW's */
	if ((ioaddr = dma_map(chan,bp)) == DMA_INV_IOADDR) {
		(*dm->ic_driver->idr_dgo)(dm,0,ioaddr,bp);
		return;
	}

	/* set up 8237 controller */
	regp->dm_mode = (mode | ((char)(bp->b_flags & B_READ) ? 
		DM_READ : DM_WRITE) | ch);
	regp->dm_ff = 0;
	regp->dm_chan[ch].dm_base = ioaddr >> 1;
	regp->dm_chan[ch].dm_base = ioaddr >> (8+1);

	regp->dm_ff = 0;
	regp->dm_chan[ch].dm_count = (len-1) >> 1;
	regp->dm_chan[ch].dm_count = (len-1) >> (8+1);

	/* call xxdgo */
	(*dm->ic_driver->idr_dgo)(dm,len,ioaddr,bp);
}

/*
 * Set up the TCW's for page mode (no system controller set up necessary)
 */
LOCAL
dma_set_page(dm)
	struct iocc_ctlr *dm;
{
	struct buf *bp = dm->ic_dmabuf;

	short chan = dm->ic_dmachannel;
	unsigned ioaddr;

	DEBUGF(dmadebug,
		printf("dma_set_page : dev=%s%d chan=%d flags=0x%b \n",
		dm->ic_driver->idr_mname,dm->ic_ctlr,dm->ic_dmachannel,
		dm->ic_dmaflags,DMA_FLAGS)
	);

	/* setup IOCC registers	*/
	DMA_SET_PAGE_MODE(chan);

	/* we own the channel, so all TCW's are fair game */
	iocc_hdr[chan].dh_pagebitmask[0] = 0;
	iocc_hdr[chan].dh_pagebitmask[1] = 0;

	/*
	 * NULL bp implies the device just wants the channel. call dgo
	 * to tell it that is has the channel.
	 */
	if (bp == NULL) {
		(*dm->ic_driver->idr_dgo)(dm,0,0,bp);
		return;
	}

	/*
	 * allocate TCW's
	 */
	if ((ioaddr = dma_map(chan,bp)) == DMA_INV_IOADDR) {
		(*dm->ic_driver->idr_dgo)(dm,0,ioaddr,bp);
		return;
	}

	/* call xxdgo */
	(*dm->ic_driver->idr_dgo)(dm,bp->b_bcount,ioaddr,bp);
}

/* region mode not coded yet */
LOCAL
dma_set_region(dm)
	struct iocc_ctlr *dm;
{
#ifdef REGION
	register struct iocc_hd *dhp = &iocc_hdr[dm->ic_dmachannel];
	register struct buf *bp = dm->ic_dmabuf;

	DEBUGF(dmadebug,
		printf("dma_set_region : dev=%s%d chan=%d flags=0x%b\n",
		dm->ic_driver->idr_mname,dm->ic_ctlr,dm->ic_dmachannel,
		dm->ic_dmaflags,DMA_FLAGS);
	);

	/* setup IOCC registers	*/
	DMA_SET_REGION_MODE(chan);

	/*
	 * NULL bp implies the device just wants the channel. call dgo
	 * to tell it that is has the channel.
	 */
	if (bp == NULL) {
		(*dm->ic_driver->idr_dgo)(dm,0,0,bp);
		return;
	}

	iocc_hdr[chan].hd_regionbitmask = 0;
	if ((ioaddr = dma_map(chan,bp) == DMA_INV_IOADDR) {
		(*dm->ic_driver->idr_dgo)(dm,0,ioaddr,bp);
		return;
	}

	(*dm->ic_driver->idr_dgo)(dm,bp->b_bcount,ioaddr,bp);
#else
	dma_set_page(dm);	/* use page mode if Region mode not defined */
#endif
}

/* region mode not coded yet */
LOCAL
dma_release_region(dm)
	struct iocc_ctlr *dm;
{
#ifdef REGION
	register struct iocc_hd *dhp = &iocc_hdr[dm->ic_dmachannel];
	register struct buf *bp = dm->ic_dmabuf;

	DEBUGF(dmadebug,
		printf("dma_release_region : dev=%s%d chan=%d flags=0x%b\n",
		dm->ic_driver->idr_mname,dm->ic_ctlr,dm->ic_dmachannel,
		dm->ic_dmaflags,DMA_FLAGS);
	);

	/* The following is too slow. Once DMA region is working, this
	 * can be optimized.
	 */

	for (i=0; i < TCW_GROUPS; i++) {
		if ( chan_region_map[i] != 0) {
			for (j=0; j < TCW_GROUP_SIZE; j++) {
				if (chan_region_map[i] & (1 << j)) {
					dma_free_map(dm->ic_dmachannel,
					 (i * TCW_GROUPS)+j) << TCW_REGIONSHIFT,
					 TCW_PAGE_SIZE);
				}
			}
		}
	}
	return;
#endif
}

/* start dma transfer */
dma_go(channel)
	short channel;
{

	register struct iocc_hd *dhp = &iocc_hdr[channel];
	register caddr_t regp = dhp->dh_8237_reg;
	register char status;

	DEBUGF(dmadebug,
		printf("dma_go called for channel=%d\n",channel);
	);

	/* enable specific channel */
	if (DMA_CTLR_1(channel)) {
 		status = ((struct dma_8bit_device volatile *)regp)->dm_cmd;
		((struct dma_8bit_device volatile *)regp)->dm_smask = 
		 DM_CHAN_ENABLE | dhp->dh_subchan;
	} else  if (DMA_CTLR_2(channel)) {
 		status = ((struct dma_16bit_device volatile *)regp)->dm_cmd;
		((struct dma_16bit_device volatile *)regp)->dm_smask = 
		 DM_CHAN_ENABLE | dhp->dh_subchan;
	} else {
		status = 0;
		*(char volatile *) regp = DM_CHAN8_ENABLE;
	}
	DEBUGF(dmadebug,
		printf("dma_go done for channel=%d\n",channel);
	);
	return(status);
}

dma_int(unit,irq)
{
	register struct iocc_ctlr *dmp;
	register int i;

	DEBUGF(dmadebug,
		printf("dma_int called.\n");
	);

	/* don't go through the loop if no one needs an int */
	if (dma_need_int) {
		for (i = DMA_CHAN0; i <= DMA_CHAN8; i++) {
			if (dma_need_int & (1 << i)) {
				/* call the device interrupt routine */
				dmp = iocc_hdr[i].dh_actf;
				if ((*dmp->ic_driver->idr_intr)
					(dmp->ic_ctlr,irq) == INT_SERVICED) {
					return(INT_SERVICED);
				}
			}
		}
	}

	/* the dma must claim all interrupts on this level */
	return(INT_SERVICED);
}




/*  This routine will be called from the requesting device interrupt handler*/
dma_done(channel)
	int channel;
{

	register struct iocc_hd *dhp = &iocc_hdr[channel];
	register caddr_t regp = dhp->dh_8237_reg;
	struct iocc_ctlr *dmp = dhp->dh_actf;
	struct dma_callback *callback;
	char ch = dhp->dh_subchan;
	int  s;

	DEBUGF(dmadebug,
		printf("dma_done called for channel=%d\n",channel);
	);

	/* tell dma_start as soon as possible that this channel is free */
	if (dhp->dh_state & DMA_EXCLUSIVE_RUNNING) {
		dhp->dh_state &= ~(DMA_EXCLUSIVE_RUNNING | DMA_EXCLUSIVE_QUEUED);
		/* notify those devices which were stuck on this channel */
		for(callback = dhp->dh_callfwd; callback != NULL; 
		 callback=callback->d_next)
			(*callback->d_wakeup)(callback->d_info);
		dhp->dh_callfwd = NULL;
	}

	/* disable specific channel */
	if (DMA_CTLR_1(channel)) {
 		dma_junk = ((struct dma_8bit_device volatile *)regp)->dm_cmd;
		((struct dma_8bit_device volatile *)regp)->dm_smask = 
		 DM_CHAN_DISABLE | ch;
	} else if (DMA_CTLR_2(channel)) {
 		dma_junk = ((struct dma_16bit_device volatile *)regp)->dm_cmd;
		((struct dma_16bit_device volatile *)regp)->dm_smask = 
		 DM_CHAN_DISABLE | ch;
	} else {
		*(char volatile *) regp = DM_CHAN8_DISABLE;
	}

	/* dequeue and dmaint routines */
	if (dmp->ic_dmaflags & DMA_CANTINT) {
		s = spl_dma();
		dma_need_int &= ~(1 << channel);
		splx(s);
	}

	/* release region mode resources */
	if (dmp->ic_dmaflags &  DMA_REGION)
		dma_release_region(dmp);

	/* check if more work need to be done */
	s = spl_dma();
	if ((dhp->dh_actf = dmp->ic_dmaforw) == NULL) {
		dhp->dh_actl = NULL;
	}

	if (dhp->dh_actf != NULL) {
		splx(s);
		dma_start(channel);
	} else {
		dhp->dh_state &= ~DMA_BUSY;
		/*
		 * This channel is no longer using dma.
		 * it's safe for operations which the dma
		 * can not co-exist with to continue. (if
		 * the other channels are also idle).
		 */
		dma_in_use--;
		splx(s);
	}
	return;

}

/*
 * dma_wait sets up calls from dma_done when exclusive devices
 * finally release the channel.
 */
dma_wait(channel,callback)
	short channel;
	struct dma_callback *callback;
{
	register struct iocc_hd *dhp = &iocc_hdr[channel];
	int s = spl_dma();

	if ((dhp->dh_state & DMA_EXCLUSIVE_RUNNING) == 0) {
		(*callback->d_wakeup)(callback->d_info);
		splx(s);
		return;
	}
	callback->d_next = NULL;
	if (dhp->dh_callfwd == NULL) {
		dhp->dh_callfwd = callback;
		dhp->dh_calllst = callback;
	} else {
		dhp->dh_calllst->d_next = callback;
		dhp->dh_calllst = callback;
	}
	splx(s);
}


/*
 * The following minphys routines are for use in conjunction with
 * physio. The dma_minphyXXXX routines are not called directly.
 * instead pass physio the value returned by dma_get_minphys
 * (which will point to the right routine.
 */
unsigned
dma_minphys8(bp)
	struct buf *bp;
{
	unsigned	max = DM_MAXPHYS8;
	unsigned	displacement = (unsigned)bp->b_un.b_addr & TCW_PAGEMASK;

	/* max transfer size is displacement dependent */
	if (displacement != 0) {
		max -= (TCW_PAGESIZE - displacement);
	}
	if (bp->b_bcount > max)
		bp->b_bcount = max;
}

unsigned
dma_minphys16(bp)
	struct buf *bp;
{
	unsigned	max = DM_MAXPHYS16;
	unsigned	displacement = (unsigned)bp->b_un.b_addr & TCW_PAGEMASK;

	/* max transfer size is displacement dependent */
	if (displacement != 0) {
		max -= (TCW_PAGESIZE - displacement);
	}
	if (bp->b_bcount > max)
		bp->b_bcount = max;
}

unsigned
dma_minphyspage(bp)
	struct buf *bp;
{
	unsigned	max = DM_MAXPHYSPAGE;
	unsigned	displacement = (unsigned)bp->b_un.b_addr & TCW_PAGEMASK;

	/* max transfer size is displacement dependent */
	max -= displacement;
	if (bp->b_bcount > max)
		bp->b_bcount = max;
}

#ifdef REGION
/*
 * this really need to use the maximum free TCW space
 * not the total TCW space.
 */
unsigned
dma_minphysregion(bp)
	struct buf *bp;
{
	if (bp->b_bcount > DM_MAXPHYSREGION)
		bp->b_bcount = DM_MAXPHYSREGION;
}
#endif

/* dma_minphys */
unsigned
(*dma_get_minphys(dm))()
	struct iocc_ctlr *dm;
{
	if ((dm->ic_dmaflags & DM_XFER) == DMA_CASCADE) {
#ifdef REGION
		if (dm->ic_dmaflags & DMA_REGION) {
			return(dma_minphysregion);
		} else {
#endif
			return(dma_minphyspage);
#ifdef REGION
		}
#endif
	} else {
		if (dm->ic_dmachannel < DMA_CHAN4) {
			return(dma_minphys8);
		} else {
			return(dma_minphys16);
		}
	}
}
#endif IBMRTPC
