/*
 * SoftPC-AT Revision 2.0
 *
 * Title	: IBM PC-AT DMA Adaptor declarations
 *
 * Description	: This module contains declarations that are used in
 *		  accessing the DMA Adaptor emulation
 *
 * Author(s)	: Ross Beresford
 *
 * Notes	: For a detailed description of the IBM DMA Adaptor,
 *		  and the INTEL DMA Controller chips refer to the following 
 *		  documents:
 *
 *		  - IBM PC/AT Technical Reference Manual
 *				(Section 1-9 Direct Memory Access)
 *		  - INTEL Microsystems Components Handbook
 *				(Section 2-52 DMA Controller 8237A)
 *
 *		  A more succinct account of the DMA adaptor follows:
 *

 * DMA Adaptor:
 * -----------
 *
 * The DMA "adaptor" is really just a part of the AT System Board. This
 * is its block diagram:
 *
 *                +-----------+                                             
 *        000-01F |8237A-5 | 0|                                 A0-A7       
 * IOS <-+------> |  DMA   | 1| -------------------------------------+      
 *       |        | CNTRL  | 2|      +-------+                       |      
 *       |        |   0    | 3| ---> |ALS 573|                 A8-A15|      
 *       |        +-----------+      |  DMA  | ----------------------+      
 *       |           ^               |ADDRESS|                       |      
 *       |            \              | LATCH |      +-------+        |      
 *       |             \ cascade     +-------+      | LS612 |        |      
 *       |080-09F       \                           |       | A16-A23|      
 *       +----------------------------------------> |  DMA  | -------+      
 *       |                \                         |  PAGE | A17-A23|      
 *       |        +-----------+                     |  REG  |        |      
 *       |        |8237A-5 | 4|                     +-------+        |      
 *       +------> |  DMA   | 5| -------------------------------------+      
 *        0C0-0DF | CNTRL  | 6|      +-------+                 A9-A16|      
 *                |   1    | 7| ---> |ALS 573|                       |      
 *                +-----------+      |  DMA  | ----------------------+-> SAS
 *                                   |ADDRESS|                  A1-A8       
 *                                   | LATCH |                              
 *                                   +-------+                              

 * DMA Controllers:
 * ---------------
 *
 * The 8237A-5 DMA controller chips used are restricted to 4 DMA
 * channels each: thus, 2 DMA controller chips are required to give the
 * AT's 7 DMA channels.
 *
 * The 8237A-5 DMA controller chips support more than one device by
 * "cascading" them: that is, connecting the control inputs of the
 * "additional" device to the control outputs of the "initial" device.
 * One channel in the initial device must be dedicated to serving
 * each additional device by setting it to "cascade mode".
 *
 * On the AT system board, channel 4 of DMA Controller 1 is set to
 * "cascade mode", and serves DMA Controller 0.
 *
 * Channels 0-3 (on DMA Controller 0) are used to support 8-bit I/O
 * devices and provide transfers of up to 64K bytes: the 16-bit addresses
 * generated by the DMA Controller are mapped directly onto bits A0-A15
 * of a 24 bit system address.
 *
 * Channels 5-7 (on DMA Controller 1) are used to support 16-bit I/O
 * devices and provide transfers of up to 128K bytes: the 16-bit addresses
 * generated by the DMA Controller are mapped onto bits A1-A16 of a
 * 24 bit system address, and bit A0 is set to 0.
 *
 * The state of a controller is defined by a set of internal registers
 * and a flip-flop: the internal registers are used in setting up and
 * performing a DMA transfer; the First/Last Flip-Flop controls which
 * byte of a 16 bit address is accessed from the I/O bus.
 *
 * The state information for a controller is represented by a DMA_CNTRL
 * object.

 * DMA Address Latches:
 * -------------------
 *
 * Each DMA Controller has an ALS573 DMA Address Latch associated with
 * it. These are required because of a restriction in the design of the
 * DMA Controller: in order to reduce its pin count it does not directly
 * output bits A8-A15 onto the address bus, but uses an external latch.
 * 
 * Since the address latches do not have any separate functional
 * identity, their state and behaviour is included in with the
 * controllers.

 * DMA Page Registers:
 * ------------------
 *
 * An LS612 DMA Page Register chip is used to establish the constant top
 * bits of the system address for a DMA transfer; each DMA channel has a
 * byte register in the chip for this purpose.
 * 
 * In DMA channels 0-3, bits A16-A23 of the 24 bit system address are
 * established from bits D0-D7 of the page register for the channel.
 * 
 * In DMA channels 5-7, bits A17-A23 of the 24 bit system address are
 * established from bits D1-D7 of the page register for the channel:
 * Bit D0 is not used for these channels, since bit A16 of the system
 * address is established by the DMA controller for these channels.
 *
 * The state information for the page register chip is represented
 * by a DMA_PAGE object.
 */ 

/* SccsID[]="@(#)dma.h	1.7 11/10/92 Copyright Insignia Solutions Ltd."; */

/*
 * ============================================================================
 * Structure/Data definitions
 * ============================================================================
 */

/*
 * DMA CONTROLLER
 */

/* the number of channels per controller */
#define	DMA_CONTROLLER_CHANNELS	4

/* a flip-flop; it toggles between the values 0 and 1 */
typedef	unsigned int	DMA_FLIP_FLOP;

/* a DMA address; it is indexed by the flip-flop value */
typedef	half_word	DMA_WORD[2];

/* a DMA command */
#ifdef BIT_ORDER1
typedef union
{
	half_word all;
	struct
	{
		 HALF_WORD_BIT_FIELD DACK_high:1;       /* not supported */
		 HALF_WORD_BIT_FIELD DREQ_high:1;	/* not supported */
		 HALF_WORD_BIT_FIELD extended_write:1;  /* not supported */
		 HALF_WORD_BIT_FIELD rotate_priority:1;	  
		 HALF_WORD_BIT_FIELD compressed_timing:1;/* not supported */
		 HALF_WORD_BIT_FIELD controller_disable:1;
		 HALF_WORD_BIT_FIELD ch0_address_hold:1;/* not supported */
		 HALF_WORD_BIT_FIELD mm_enable:1;       /* not supported */
       } bits;
} DMA_COMMAND;
#endif
#ifdef BIT_ORDER2
typedef union
{
 	half_word all;
	struct
	{
		 HALF_WORD_BIT_FIELD mm_enable:1;       /* not supported */
		 HALF_WORD_BIT_FIELD ch0_address_hold:1;/* not supported */
		 HALF_WORD_BIT_FIELD controller_disable:1;
		 HALF_WORD_BIT_FIELD compressed_timing:1; /* not supported */
		 HALF_WORD_BIT_FIELD rotate_priority:1;	  
		 HALF_WORD_BIT_FIELD extended_write:1;  /* not supported */
		 HALF_WORD_BIT_FIELD DREQ_high:1;       /* not supported */
		 HALF_WORD_BIT_FIELD DACK_high:1;	/* not supported */
	} bits;
} DMA_COMMAND;
#endif

/* a DMA mode */
#ifdef BIT_ORDER1
typedef union
{
 	half_word all;
	struct
	{
		 HALF_WORD_BIT_FIELD mode:2;	    /* single/block/demand/cascade */
		 HALF_WORD_BIT_FIELD address_dec:1; /* inc or dec count  */
		 HALF_WORD_BIT_FIELD auto_init:1;   /* autoinitialize ?  */
		 HALF_WORD_BIT_FIELD transfer_type:2; /* read/write/verify */
		 HALF_WORD_BIT_FIELD scratch:2;     /* channel select mapped here */
       } bits;
} DMA_MODE;
#endif
#ifdef BIT_ORDER2
typedef union
{
 	half_word all;
	struct
	{
		 HALF_WORD_BIT_FIELD scratch:2;     /* channel select mapped here */
		 HALF_WORD_BIT_FIELD transfer_type:2; /* read/write/verify */
		 HALF_WORD_BIT_FIELD auto_init:1;   /* autoinitialize ?  */
		 HALF_WORD_BIT_FIELD address_dec:1; /* inc or dec count  */
		 HALF_WORD_BIT_FIELD mode:2;	    /* single/block/demand/cascade */
       } bits;
} DMA_MODE;
#endif

/* DMA Mode register mode bit settings */
#define DMA_DEMAND_MODE		0
#define DMA_SINGLE_MODE		1
#define DMA_BLOCK_MODE		2
#define DMA_CASCADE_MODE	3

/* DMA Mode register transfer type bit settings */
#define DMA_VERIFY_TRANSFER	0
#define DMA_WRITE_TRANSFER	1
#define DMA_READ_TRANSFER	2
#define DMA_ILLEGAL_TRANSFER	3

/* a DMA status */
#ifdef BIT_ORDER1
typedef union
{
	half_word all;
	struct
	{
		HALF_WORD_BIT_FIELD request:4;		/* request pending   */
		HALF_WORD_BIT_FIELD terminal_count:4;	/* terminal count reached */
	} bits;
} DMA_STATUS;
#endif
#ifdef BIT_ORDER2
typedef union
{
	half_word all;
	struct {
		HALF_WORD_BIT_FIELD terminal_count:4;	/* terminal count reached */
		HALF_WORD_BIT_FIELD request:4;		/* request pending   */
	} bits;
} DMA_STATUS;
#endif

/* a DMA mask register (only the bottom 4 bits are meaningful) */
typedef	half_word	DMA_MASK;

/* a DMA Controller state */
typedef struct
{
	DMA_WORD	base_address[DMA_CONTROLLER_CHANNELS];
	DMA_WORD	base_count[DMA_CONTROLLER_CHANNELS];
	DMA_WORD	current_address[DMA_CONTROLLER_CHANNELS];
	DMA_WORD	current_count[DMA_CONTROLLER_CHANNELS];
	DMA_WORD	temporary_address[DMA_CONTROLLER_CHANNELS];
	DMA_WORD	temporary_count[DMA_CONTROLLER_CHANNELS];
	DMA_STATUS	status;
	DMA_COMMAND	command;
	half_word	temporary;
	DMA_MODE	mode[DMA_CONTROLLER_CHANNELS];
	DMA_MASK	mask;
	half_word	request;
	DMA_FLIP_FLOP	first_last;
} DMA_CNTRL;

/*
 * DMA PAGE REGISTERS
 */

/* the number of channels per page register chip */
#define	DMA_PAGE_CHANNELS	16

/* a DMA Page Register chip state */
typedef	struct
{
	half_word	page[DMA_PAGE_CHANNELS];
} DMA_PAGE;

/*
 * DMA ADAPTOR
 */

/* the number of controllers per adaptor */
#define	DMA_ADAPTOR_CONTROLLERS	2

/* the numbering convention for the controllers */
#define	DMA_CONTROLLER	0
#define	DMA1_CONTROLLER	1

/* a DMA adaptor state */
typedef	struct
{
	DMA_CNTRL	controller[DMA_ADAPTOR_CONTROLLERS];
	DMA_PAGE	pages;
} DMA_ADAPT;

/* the number of logical channels per adaptor */
#define	DMA_ADAPTOR_CHANNELS	\
	(DMA_ADAPTOR_CONTROLLERS * DMA_CONTROLLER_CHANNELS)

/* macros for converting between logical and physical channel numbers */
#define	dma_logical_channel(controller, channel)	\
		(controller*DMA_CONTROLLER_CHANNELS + channel)
#define	dma_physical_channel(channel)	\
		(channel%DMA_CONTROLLER_CHANNELS)
#define	dma_physical_controller(channel)	\
		(channel/DMA_CONTROLLER_CHANNELS)

/* macro for building a system address dependent on the channel */
#define	dma_system_address(channel, page, offset) \
	( (channel < 4) \
	? (((sys_addr)(page & 0xff) << 16) | ((sys_addr)(offset & 0xffff) << 0)) \
	: (((sys_addr)(page & 0xfe) << 16) | ((sys_addr)(offset & 0xffff) << 1)) )

/* macro for determining the DMA unit size dependent on the channel */
#define	dma_unit_size(channel) ( (channel < 4) ? 1 : 2)

/* the channel assignments */
#define	DMA_RESERVED_CHANNEL_0	0
#define	DMA_SDLC_CHANNEL	1
#define	DMA_DISKETTE_CHANNEL	2
#define	DMA_DISK_CHANNEL	3
#define	DMA_CASCADE_CHANNEL	4
#define	DMA_RESERVED_CHANNEL_5	5
#define	DMA_RESERVED_CHANNEL_6	6
#define	DMA_RESERVED_CHANNEL_7	7
#define DMA_FAKE_CHANNEL_1	8
#define DMA_FAKE_CHANNEL_2	9
#define DMA_FAKE_CHANNEL_3	10
#define DMA_FAKE_CHANNEL_4	11
#define DMA_FAKE_CHANNEL_5	12
#define DMA_FAKE_CHANNEL_6	13
#define DMA_FAKE_CHANNEL_7	14
#define DMA_REFRESH_CHANNEL	15

/* these masks define the redundant bits of the I/O addresses */
#define	DMA_REDUNDANT_BITS	0x10
#define	DMA1_REDUNDANT_BITS	0x01
#define	DMA_PAGE_REDUNDANT_BITS	0x10

/*
 * DMA Address - the following union is used only in the BIOS functions
 * to help determine how a system address should be expressed in terms
 * of its component parts.
 * It is not used within the DMA emulation
 */
#ifdef BIGEND
typedef union
{
	sys_addr all;
	half_word array[4];
	struct
	{
		half_word pad;
		half_word page;
		half_word high;
		half_word low;
	} parts;
	struct
	{
		word high;
		word low;
	} words;
} DMA_ADDRESS;
#endif
#ifdef LITTLEND
typedef union
{
	sys_addr all;
	half_word array[4];
	struct
	{
		half_word low;
		half_word high;
		half_word page;
		half_word pad;
	} parts;
	struct
	{
		word low;
		word high;
	} words;
} DMA_ADDRESS;
#endif

/*
 * ============================================================================
 * External declarations and macros
 * ============================================================================
 */

/*
 *	void dma_init()
 *	{
 *		This function performs several distinct initialisation
 *		tasks associated with the DMA adaptor:
 *
 *		the DMA Controller chips and the DMA Page Register chip
 *		are connected to the I/O bus;
 *
 *		a hardware reset is performed for each DMA Controller
 *		chip;
 *	}
 */
IMPORT VOID dma_init IPT0();

/*
 *	void dma_post()
 *	{
 *		the mode register for each DMA channel is set up to the
 *		normal value for an AT.
 *	}
 */
IMPORT VOID dma_post IPT0();

/*
 *	void dma_inb(port, value)
 *	io_addr port;
 *	half_word *value;
 *	{
 *		This function is invoked when a read is performed on an
 *		I/O address "port" in the range of one of the DMA
 *		Controller chips. dma_inb() may also be called directly
 *		from the BIOS.
 *
 *		The function maps the I/O address to the particular DMA
 *		controller and returns the value of the requested
 *		register in "*value".
 *	}
 */
IMPORT VOID dma_inb IPT2(io_addr, port, half_word *, value);

/*
 *	void dma_outb(port, value)
 *	io_addr port;
 *	half_word value;
 *	{
 *		This function is invoked when a write is performed to an
 *		I/O address "port" in the range of one of the DMA
 *		Controller chips. dma_outb() may also be called directly
 *		from the BIOS.
 *
 *		The function maps the I/O address to the particular DMA
 *		controller and sets the requested register to "value".
 *	}
 */
IMPORT VOID dma_outb IPT2(io_addr, port, half_word, value);

/*
 *	void dma_page_inb(port, value)
 *	io_addr port;
 *	half_word *value;
 *	{
 *		This function is invoked when a read is performed on an
 *		I/O address "port" in the range of the DMA Page Register
 *		chip. dma_page_inb() may also be called directly from the
 *		BIOS.
 *
 *		The function maps the I/O address to a particular DMA
 *		page register and returns its value in "*value".
 *	}
 */
IMPORT VOID dma_page_inb IPT2(io_addr, port, half_word *, value);

/*
 *	void dma_page_outb(port, value)
 *	io_addr port;
 *	half_word value;
 *	{
 *		This function is invoked when a write is performed to an
 *		I/O address "port" in the range of the DMA Page Register
 *		chip. dma_page_outb() may also be called directly from the
 *		BIOS.
 *
 *		The function maps the I/O address to a particular DMA
 *		page register and sets it to "value".
 *	}
 */
IMPORT VOID dma_page_outb IPT2(io_addr, port, half_word, value);

/*
 *	dma_request(channel, hw_buffer, length)
 *	half_word channel;
 *	char *hw_buffer;
 *	word length;
 *	{
 *		I/O devices call this function to initiate a DMA
 *		transfer. It is analogous to raising the DREQ line
 *		on the DMA controller for the channel.
 *
 *		The DMA channel to be used is passed in "channel";
 *		"hw_buffer" is the address in the device's memory
 *		space of the buffer that should be read from or written
 *		into the system address space; "length" is the maximum
 *		number of units to be transferred: the unit is a
 *		half_word for channels 0-3 and a word for channels 5-7.
 *
 *		The function returns TRUE if more transfers can be
 *		accepted or FALSE if the terminal count has been
 *		reached.
 *	}
 */
IMPORT int dma_request IPT3(half_word, port, char *, hw_buffer, word, length);

/*
 *	void dma_enquire(channel, address, length)
 *	half_word channel;
 *	sys_addr *address;
 *	word *length;
 *	{
 *		I/O devices call this function to find out how the
 *		DMA controller has been programmed up to do a transfer.
 *
 *		The DMA channel to be accessed is passed in "channel";
 *		the address in system address space that will be read
 *		or written in the transfer is returned in "*address";
 *		"length" is the number of units that have been
 *		requested: the unit is a half_word for channels 0-3
 *		and a word for channels 5-7.
 *	}
 */
IMPORT VOID dma_enquire IPT3(half_word, channel, sys_addr *, address, word *, length);

#ifdef NTVDM
/*
 * BOOL dmaGetAdaptor
 *
 * Used by MS for third party Vdds to retrieve current DMA settings
 *
 * entry: void
 * exit : DMA_ADAPT * , pointer to the DMA_ADAPT structure
 *
 */
DMA_ADAPT *dmaGetAdaptor();
#endif	/* NTVDM */
