mirror of https://github.com/lianthony/NT4.0
925 lines
22 KiB
925 lines
22 KiB
#include "insignia.h"
|
|
#include "host_def.h"
|
|
/*
|
|
* SoftPC-AT Revision 3.0
|
|
*
|
|
* Title : IBM PC-AT DMA Adaptor Functions
|
|
*
|
|
* Description : This module contains functions that can be used to
|
|
* access the DMA Adaptor emulation
|
|
*
|
|
* Author : Ross Beresford
|
|
*
|
|
* Notes : The external interface to these functions is defined
|
|
* in the associated header file
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* static char SccsID[]="@(#)at_dma.c 1.15 12/17/93 Copyright Insignia Solutions Ltd.";
|
|
*/
|
|
|
|
|
|
#ifdef SEGMENTATION
|
|
/*
|
|
* The following #include specifies the code segment into which this
|
|
* module will by placed by the MPW C compiler on the Mac II running
|
|
* MultiFinder.
|
|
*/
|
|
#include "AT_STUFF.seg"
|
|
#endif
|
|
|
|
/*
|
|
* System include files
|
|
*/
|
|
#include <stdio.h>
|
|
#include StringH
|
|
#include TypesH
|
|
|
|
/*
|
|
* SoftPC include files
|
|
*/
|
|
#include "xt.h"
|
|
#include "sas.h"
|
|
#include "ios.h"
|
|
#include "gmi.h"
|
|
#include CpuH
|
|
#include "trace.h"
|
|
#include "dma.h"
|
|
#include "debug.h"
|
|
|
|
/*
|
|
* ============================================================================
|
|
* Local static data and defines
|
|
* ============================================================================
|
|
*/
|
|
|
|
/* the DMA adaptor state */
|
|
static DMA_ADAPT adaptor = { 0 };
|
|
|
|
/* local function to do the actual transfer of data */
|
|
LOCAL void do_transfer IPT6(int, type, int, decrement,
|
|
sys_addr, dma_addr, char *, hw_buffer, unsigned long, length,
|
|
unsigned long, size);
|
|
|
|
LOCAL void bwd_dest_copy_to_M IPT3(host_addr, s, sys_addr, d, sys_addr, l);
|
|
|
|
LOCAL void bwd_dest_copy_from_M IPT3(sys_addr, s, host_addr, d,
|
|
sys_addr, l);
|
|
|
|
#ifdef LIM
|
|
static int lim_active = 0;
|
|
#endif /* LIM */
|
|
|
|
|
|
/*
|
|
* ============================================================================
|
|
* External functions
|
|
* ============================================================================
|
|
*/
|
|
|
|
#ifdef LIM
|
|
/*
|
|
** Called from do_transfer() and from init_struc.c in delta
|
|
** for the 800 port.
|
|
** lim_active is a static int
|
|
*/
|
|
GLOBAL int get_lim_setup IFN0()
|
|
{
|
|
return( lim_active );
|
|
}
|
|
#endif /* LIM */
|
|
|
|
#ifdef LIM
|
|
/*
|
|
** called from emm_funcs.c
|
|
*/
|
|
GLOBAL void dma_lim_setup IFN0()
|
|
{
|
|
lim_active = 1;
|
|
}
|
|
#endif /* LIM */
|
|
|
|
GLOBAL void dma_post IFN0()
|
|
{
|
|
unsigned int chan, cntrl;
|
|
|
|
/*
|
|
* Reset the DMA Adaptor
|
|
*/
|
|
|
|
for (cntrl = 0; cntrl < DMA_ADAPTOR_CONTROLLERS; cntrl++)
|
|
{
|
|
adaptor.controller[cntrl].command.all = 0;
|
|
adaptor.controller[cntrl].status.all = 0;
|
|
adaptor.controller[cntrl].request = 0;
|
|
adaptor.controller[cntrl].temporary = 0;
|
|
adaptor.controller[cntrl].mask = ~0;
|
|
|
|
adaptor.controller[cntrl].first_last = 0;
|
|
}
|
|
|
|
/*
|
|
* Set the DMA Adaptor channel modes
|
|
*/
|
|
|
|
for (cntrl = 0; cntrl < DMA_ADAPTOR_CONTROLLERS; cntrl++)
|
|
{
|
|
for (chan = 0; chan < DMA_CONTROLLER_CHANNELS; chan++)
|
|
{
|
|
adaptor.controller[cntrl].mode[chan].all = 0;
|
|
/* put the channels into their correct mode */
|
|
if (dma_logical_channel(cntrl, chan) == DMA_CASCADE_CHANNEL)
|
|
adaptor.controller[cntrl].mode[chan].bits.mode = DMA_CASCADE_MODE;
|
|
else
|
|
adaptor.controller[cntrl].mode[chan].bits.mode = DMA_SINGLE_MODE;
|
|
}
|
|
}
|
|
}
|
|
|
|
GLOBAL void dma_inb IFN2(io_addr, port, half_word *, value)
|
|
{
|
|
register DMA_CNTRL *dcp;
|
|
|
|
note_trace0_no_nl(DMA_VERBOSE, "dma_inb() ");
|
|
|
|
/*
|
|
* Get a pointer to the controller and mask out the port's
|
|
* redundant bits.
|
|
* The first check is commented out as DMA_PORT_START is zero,
|
|
* so the check on an unsigned variable is unnecessary.
|
|
*/
|
|
if (/*port >= DMA_PORT_START &&*/ port <= DMA_PORT_END)
|
|
{
|
|
dcp = &adaptor.controller[DMA_CONTROLLER];
|
|
port &= ~DMA_REDUNDANT_BITS;
|
|
}
|
|
else
|
|
{
|
|
dcp = &adaptor.controller[DMA1_CONTROLLER];
|
|
port &= ~DMA1_REDUNDANT_BITS;
|
|
}
|
|
|
|
/*
|
|
* When the current address and word count are read, the
|
|
* first/last flip-flop for the controller is used to
|
|
* determine which byte is accessed, and is then toggled.
|
|
*/
|
|
switch (port)
|
|
{
|
|
/* read channel current address on controller 0 */
|
|
case DMA_CH0_ADDRESS:
|
|
case DMA_CH1_ADDRESS:
|
|
case DMA_CH2_ADDRESS:
|
|
case DMA_CH3_ADDRESS:
|
|
*value = dcp->current_address[(port-DMA_CH0_ADDRESS)/2][dcp->first_last];
|
|
dcp->first_last ^= 1;
|
|
break;
|
|
|
|
/* read channel current address on controller 1 */
|
|
case DMA_CH4_ADDRESS:
|
|
case DMA_CH5_ADDRESS:
|
|
case DMA_CH6_ADDRESS:
|
|
case DMA_CH7_ADDRESS:
|
|
*value = dcp->current_address[(port-DMA_CH4_ADDRESS)/4][dcp->first_last];
|
|
dcp->first_last ^= 1;
|
|
break;
|
|
|
|
/* read channel current word count on controller 0 */
|
|
case DMA_CH0_COUNT:
|
|
case DMA_CH1_COUNT:
|
|
case DMA_CH2_COUNT:
|
|
case DMA_CH3_COUNT:
|
|
*value = dcp->current_count[(port-DMA_CH0_COUNT)/2][dcp->first_last];
|
|
dcp->first_last ^= 1;
|
|
break;
|
|
|
|
/* read channel current word count on controller 1 */
|
|
case DMA_CH4_COUNT:
|
|
case DMA_CH5_COUNT:
|
|
case DMA_CH6_COUNT:
|
|
case DMA_CH7_COUNT:
|
|
*value = dcp->current_count[(port-DMA_CH4_COUNT)/4][dcp->first_last];
|
|
dcp->first_last ^= 1;
|
|
break;
|
|
|
|
/* read status register - clears terminal counts */
|
|
case DMA_SHARED_REG_A:
|
|
case DMA1_SHARED_REG_A:
|
|
*value = dcp->status.all;
|
|
dcp->status.bits.terminal_count = 0;
|
|
break;
|
|
|
|
/* read temporary register */
|
|
case DMA_SHARED_REG_B:
|
|
case DMA1_SHARED_REG_B:
|
|
*value = dcp->temporary;
|
|
break;
|
|
|
|
default:
|
|
note_trace0_no_nl(DMA_VERBOSE, "<illegal read>");
|
|
break;
|
|
}
|
|
|
|
note_trace2(DMA_VERBOSE, " port 0x%04x, returning 0x%02x", port,
|
|
*value);
|
|
}
|
|
|
|
GLOBAL void dma_outb IFN2(io_addr, port, half_word, value)
|
|
{
|
|
register DMA_CNTRL *dcp;
|
|
|
|
note_trace0_no_nl(DMA_VERBOSE, "dma_outb() ");
|
|
|
|
/*
|
|
* Get a pointer to the controller and mask out the port's
|
|
* redundant bits.
|
|
* The first check is commented out as DMA_PORT_START is zero,
|
|
* so the check on an unsigned variable is unnecessary.
|
|
*/
|
|
if (/*port >= DMA_PORT_START &&*/ port <= DMA_PORT_END)
|
|
{
|
|
dcp = &adaptor.controller[DMA_CONTROLLER];
|
|
port &= ~DMA_REDUNDANT_BITS;
|
|
}
|
|
else
|
|
{
|
|
dcp = &adaptor.controller[DMA1_CONTROLLER];
|
|
port &= ~DMA1_REDUNDANT_BITS;
|
|
}
|
|
|
|
/*
|
|
* When the current address and word count are written, the
|
|
* first/last flip-flop for the controller is used to
|
|
* determine which byte is accessed, and is then toggled.
|
|
*/
|
|
switch (port)
|
|
{
|
|
/* write channel addresseess on controller 0 */
|
|
case DMA_CH0_ADDRESS:
|
|
case DMA_CH1_ADDRESS:
|
|
case DMA_CH2_ADDRESS:
|
|
case DMA_CH3_ADDRESS:
|
|
dcp->current_address[(port-DMA_CH0_ADDRESS)/2][dcp->first_last] = value;
|
|
dcp->base_address[(port-DMA_CH0_ADDRESS)/2][dcp->first_last] = value;
|
|
dcp->first_last ^= 1;
|
|
break;
|
|
|
|
/* write channel addresses on controller 1 */
|
|
case DMA_CH4_ADDRESS:
|
|
case DMA_CH5_ADDRESS:
|
|
case DMA_CH6_ADDRESS:
|
|
case DMA_CH7_ADDRESS:
|
|
dcp->current_address[(port-DMA_CH4_ADDRESS)/4][dcp->first_last] = value;
|
|
dcp->base_address[(port-DMA_CH4_ADDRESS)/4][dcp->first_last] = value;
|
|
dcp->first_last ^= 1;
|
|
break;
|
|
|
|
/* write channel word counts on controller 0 */
|
|
case DMA_CH0_COUNT:
|
|
case DMA_CH1_COUNT:
|
|
case DMA_CH2_COUNT:
|
|
case DMA_CH3_COUNT:
|
|
dcp->current_count[(port-DMA_CH0_COUNT)/2][dcp->first_last] = value;
|
|
dcp->base_count[(port-DMA_CH0_COUNT)/2][dcp->first_last] = value;
|
|
dcp->first_last ^= 1;
|
|
break;
|
|
|
|
/* write channel word counts on controller 1 */
|
|
case DMA_CH4_COUNT:
|
|
case DMA_CH5_COUNT:
|
|
case DMA_CH6_COUNT:
|
|
case DMA_CH7_COUNT:
|
|
dcp->current_count[(port-DMA_CH4_COUNT)/4][dcp->first_last] = value;
|
|
dcp->base_count[(port-DMA_CH4_COUNT)/4][dcp->first_last] = value;
|
|
dcp->first_last ^= 1;
|
|
break;
|
|
|
|
/* write command register */
|
|
case DMA_SHARED_REG_A:
|
|
case DMA1_SHARED_REG_A:
|
|
dcp->command.all = value;
|
|
break;
|
|
|
|
/* write request register */
|
|
case DMA_WRITE_REQUEST_REG:
|
|
case DMA1_WRITE_REQUEST_REG:
|
|
/* this feature is not supported */
|
|
note_trace0_no_nl(DMA_VERBOSE, "<software DMA request>");
|
|
break;
|
|
|
|
/* write single mask register bit */
|
|
case DMA_WRITE_ONE_MASK_BIT:
|
|
case DMA1_WRITE_ONE_MASK_BIT:
|
|
if (value & 0x4)
|
|
{
|
|
/* set mask bit */
|
|
dcp->mask |= (1 << (value & 0x3));
|
|
}
|
|
else
|
|
{
|
|
/* clear mask bit */
|
|
dcp->mask &= ~(1 << (value & 0x3));
|
|
}
|
|
break;
|
|
|
|
/* write mode register */
|
|
case DMA_WRITE_MODE_REG:
|
|
case DMA1_WRITE_MODE_REG:
|
|
/* note that the bottom 2 bits of value disappear into
|
|
the mode padding */
|
|
dcp->mode[(value & 0x3)].all = value;
|
|
break;
|
|
|
|
/* clear first/last flip-flop */
|
|
case DMA_CLEAR_FLIP_FLOP:
|
|
case DMA1_CLEAR_FLIP_FLOP:
|
|
dcp->first_last = 0;
|
|
break;
|
|
|
|
/* write master clear */
|
|
case DMA_SHARED_REG_B:
|
|
case DMA1_SHARED_REG_B:
|
|
dcp->command.all = 0;
|
|
dcp->status.all = 0;
|
|
dcp->request = 0;
|
|
dcp->temporary = 0;
|
|
dcp->mask = ~0;
|
|
|
|
dcp->first_last = 0;
|
|
break;
|
|
|
|
/* clear mask register */
|
|
case DMA_CLEAR_MASK:
|
|
case DMA1_CLEAR_MASK:
|
|
dcp->mask = 0;
|
|
break;
|
|
|
|
/* write all mask register bits */
|
|
case DMA_WRITE_ALL_MASK_BITS:
|
|
case DMA1_WRITE_ALL_MASK_BITS:
|
|
dcp->mask = value;
|
|
|
|
default:
|
|
note_trace0_no_nl(DMA_VERBOSE, "<illegal write>");
|
|
break;
|
|
}
|
|
|
|
note_trace2(DMA_VERBOSE, " port 0x%04x, value 0x%02x", port, value);
|
|
}
|
|
|
|
GLOBAL void dma_page_inb IFN2(io_addr, port, half_word *, value)
|
|
{
|
|
note_trace0_no_nl(DMA_VERBOSE, "dma_page_inb() ");
|
|
|
|
/* mask out the port's redundant bits */
|
|
port &= ~DMA_PAGE_REDUNDANT_BITS;
|
|
|
|
/*
|
|
* Read the value from the appropriate page register.
|
|
* Unfortunately there does not seem to be any logical
|
|
* mapping between port numbers and channel numbers so
|
|
* we use a big switch again.
|
|
*/
|
|
switch(port)
|
|
{
|
|
case DMA_CH0_PAGE_REG:
|
|
*value = adaptor.pages.page[DMA_RESERVED_CHANNEL_0];
|
|
break;
|
|
case DMA_CH1_PAGE_REG:
|
|
*value = adaptor.pages.page[DMA_SDLC_CHANNEL];
|
|
break;
|
|
case DMA_FLA_PAGE_REG:
|
|
*value = adaptor.pages.page[DMA_DISKETTE_CHANNEL];
|
|
break;
|
|
case DMA_HDA_PAGE_REG:
|
|
*value = adaptor.pages.page[DMA_DISK_CHANNEL];
|
|
break;
|
|
case DMA_CH5_PAGE_REG:
|
|
*value = adaptor.pages.page[DMA_RESERVED_CHANNEL_5];
|
|
break;
|
|
case DMA_CH6_PAGE_REG:
|
|
*value = adaptor.pages.page[DMA_RESERVED_CHANNEL_6];
|
|
break;
|
|
case DMA_CH7_PAGE_REG:
|
|
*value = adaptor.pages.page[DMA_RESERVED_CHANNEL_7];
|
|
break;
|
|
case DMA_REFRESH_PAGE_REG:
|
|
*value = adaptor.pages.page[DMA_REFRESH_CHANNEL];
|
|
break;
|
|
case DMA_FAKE1_REG:
|
|
*value = adaptor.pages.page[DMA_FAKE_CHANNEL_1];
|
|
break;
|
|
case DMA_FAKE2_REG:
|
|
*value = adaptor.pages.page[DMA_FAKE_CHANNEL_2];
|
|
break;
|
|
case DMA_FAKE3_REG:
|
|
*value = adaptor.pages.page[DMA_FAKE_CHANNEL_3];
|
|
break;
|
|
case DMA_FAKE4_REG:
|
|
*value = adaptor.pages.page[DMA_FAKE_CHANNEL_4];
|
|
break;
|
|
case DMA_FAKE5_REG:
|
|
*value = adaptor.pages.page[DMA_FAKE_CHANNEL_5];
|
|
break;
|
|
case DMA_FAKE6_REG:
|
|
*value = adaptor.pages.page[DMA_FAKE_CHANNEL_6];
|
|
break;
|
|
case DMA_FAKE7_REG:
|
|
*value = adaptor.pages.page[DMA_FAKE_CHANNEL_7];
|
|
break;
|
|
default:
|
|
note_trace0_no_nl(DMA_VERBOSE, "<illegal read>");
|
|
break;
|
|
}
|
|
|
|
note_trace2(DMA_VERBOSE, " port 0x%04x, returning 0x%02x", port,
|
|
*value);
|
|
}
|
|
|
|
GLOBAL void dma_page_outb IFN2(io_addr, port, half_word, value)
|
|
{
|
|
note_trace0_no_nl(DMA_VERBOSE, "dma_page_outb() ");
|
|
|
|
/* mask out the port's redundant bits */
|
|
port &= ~DMA_PAGE_REDUNDANT_BITS;
|
|
|
|
/*
|
|
* Write the value into the appropriate page register.
|
|
* Unfortunately there does not seem to be any logical
|
|
* mapping between port numbers and channel numbers so
|
|
* we use a big switch again.
|
|
*/
|
|
switch(port)
|
|
{
|
|
case DMA_CH0_PAGE_REG:
|
|
adaptor.pages.page[DMA_RESERVED_CHANNEL_0] = value;
|
|
break;
|
|
case DMA_CH1_PAGE_REG:
|
|
adaptor.pages.page[DMA_SDLC_CHANNEL] = value;
|
|
break;
|
|
case DMA_FLA_PAGE_REG:
|
|
adaptor.pages.page[DMA_DISKETTE_CHANNEL] = value;
|
|
break;
|
|
case DMA_HDA_PAGE_REG:
|
|
adaptor.pages.page[DMA_DISK_CHANNEL] = value;
|
|
break;
|
|
case DMA_CH5_PAGE_REG:
|
|
adaptor.pages.page[DMA_RESERVED_CHANNEL_5] = value;
|
|
break;
|
|
case DMA_CH6_PAGE_REG:
|
|
adaptor.pages.page[DMA_RESERVED_CHANNEL_6] = value;
|
|
break;
|
|
case DMA_CH7_PAGE_REG:
|
|
adaptor.pages.page[DMA_RESERVED_CHANNEL_7] = value;
|
|
break;
|
|
case DMA_REFRESH_PAGE_REG:
|
|
adaptor.pages.page[DMA_REFRESH_CHANNEL] = value;
|
|
/* this feature is supported */
|
|
note_trace0_no_nl(DMA_VERBOSE, "<refresh>");
|
|
break;
|
|
case MFG_PORT:
|
|
/* Manufacturing port */
|
|
/* Meaningless 'checkpoint' debug removed from here STF 11/92 */
|
|
break;
|
|
case DMA_FAKE1_REG:
|
|
adaptor.pages.page[DMA_FAKE_CHANNEL_1] = value;
|
|
break;
|
|
case DMA_FAKE2_REG:
|
|
adaptor.pages.page[DMA_FAKE_CHANNEL_2] = value;
|
|
break;
|
|
case DMA_FAKE3_REG:
|
|
adaptor.pages.page[DMA_FAKE_CHANNEL_3] = value;
|
|
break;
|
|
case DMA_FAKE4_REG:
|
|
adaptor.pages.page[DMA_FAKE_CHANNEL_4] = value;
|
|
break;
|
|
case DMA_FAKE5_REG:
|
|
adaptor.pages.page[DMA_FAKE_CHANNEL_5] = value;
|
|
break;
|
|
case DMA_FAKE6_REG:
|
|
adaptor.pages.page[DMA_FAKE_CHANNEL_6] = value;
|
|
break;
|
|
case DMA_FAKE7_REG:
|
|
adaptor.pages.page[DMA_FAKE_CHANNEL_7] = value;
|
|
break;
|
|
default:
|
|
note_trace0_no_nl(DMA_VERBOSE, "<illegal write>");
|
|
break;
|
|
}
|
|
|
|
note_trace2(DMA_VERBOSE, " port 0x%04x, value 0x%02x", port, value);
|
|
}
|
|
|
|
GLOBAL int dma_request IFN3(half_word, channel, char *, hw_buffer,
|
|
word, length)
|
|
{
|
|
DMA_CNTRL *dcp;
|
|
unsigned int chan;
|
|
word offset, count;
|
|
sys_addr munch, split_munch1, split_munch2, address;
|
|
unsigned int size;
|
|
int result = TRUE;
|
|
|
|
note_trace3(DMA_VERBOSE,
|
|
"dma_request() channel %d, hw_buffer 0x%08x+%04x",
|
|
channel, hw_buffer, length);
|
|
|
|
/* get a pointer to the controller, the physical channel
|
|
number, and the unit size for the channel */
|
|
dcp = &adaptor.controller[dma_physical_controller(channel)];
|
|
chan = dma_physical_channel(channel);
|
|
size = dma_unit_size(channel);
|
|
|
|
/* get out if the whole DMA controller is disabled or if DMA
|
|
requests are disabled for the channel */
|
|
if ( (dcp->command.bits.controller_disable == 0)
|
|
&& ((dcp->mask & (1 << chan)) == 0) )
|
|
{
|
|
/* get the working copies of the DMA offset and count */
|
|
offset = ( ( (unsigned int)dcp->current_address[chan][1] << 8)
|
|
| (dcp->current_address[chan][0] << 0) );
|
|
count = ( ( (unsigned int)dcp->current_count[chan][1] << 8)
|
|
| (dcp->current_count[chan][0] << 0) );
|
|
|
|
/* get the DMA munch size; it is the count programmed
|
|
into the registers, up to the limit available in the
|
|
device's buffer; NB for a count of n, n+1 units will
|
|
actually be transferred */
|
|
munch = (sys_addr)count + 1;
|
|
if (munch > length)
|
|
munch = length;
|
|
|
|
/* get the base address for the DMA transfer in
|
|
system address space */
|
|
address = dma_system_address(channel,
|
|
adaptor.pages.page[channel], offset);
|
|
if (dcp->mode[chan].bits.address_dec == 0)
|
|
{
|
|
/* increment memory case - check for address wrapping */
|
|
if ((sys_addr)offset + munch > 0x10000L)
|
|
{
|
|
/* transfer must be split */
|
|
split_munch1 = 0x10000L - (sys_addr)offset;
|
|
split_munch2 = munch - split_munch1;
|
|
|
|
/* do the first transfer */
|
|
do_transfer
|
|
(
|
|
dcp->mode[chan].bits.transfer_type,
|
|
dcp->mode[chan].bits.address_dec,
|
|
address,
|
|
hw_buffer,
|
|
split_munch1,
|
|
size
|
|
);
|
|
|
|
/* get addresses for second transfer */
|
|
address = dma_system_address(channel,
|
|
adaptor.pages.page[channel], 0);
|
|
hw_buffer += split_munch1*size;
|
|
|
|
/* do the second transfer */
|
|
do_transfer
|
|
(
|
|
dcp->mode[chan].bits.transfer_type,
|
|
dcp->mode[chan].bits.address_dec,
|
|
address,
|
|
hw_buffer,
|
|
split_munch2,
|
|
size
|
|
);
|
|
}
|
|
else
|
|
{
|
|
/* no wrap - do the transfer */
|
|
do_transfer
|
|
(
|
|
dcp->mode[chan].bits.transfer_type,
|
|
dcp->mode[chan].bits.address_dec,
|
|
address,
|
|
hw_buffer,
|
|
munch,
|
|
size
|
|
);
|
|
}
|
|
|
|
/* get the final offset */
|
|
offset += munch;
|
|
count -= munch;
|
|
}
|
|
else
|
|
{
|
|
/* decrement memory case - check for address wrapping */
|
|
if ((sys_addr)offset < munch)
|
|
{
|
|
/* transfer must be split */
|
|
split_munch1 = (sys_addr)offset;
|
|
split_munch2 = munch - split_munch1;
|
|
|
|
/* do the first transfer */
|
|
do_transfer
|
|
(
|
|
dcp->mode[chan].bits.transfer_type,
|
|
dcp->mode[chan].bits.address_dec,
|
|
address,
|
|
hw_buffer,
|
|
split_munch1,
|
|
size
|
|
);
|
|
|
|
/* get addresses for second transfer */
|
|
address = dma_system_address(channel,
|
|
adaptor.pages.page[channel], 0xffff);
|
|
hw_buffer += split_munch1*size;
|
|
|
|
/* do the second transfer */
|
|
do_transfer
|
|
(
|
|
dcp->mode[chan].bits.transfer_type,
|
|
dcp->mode[chan].bits.address_dec,
|
|
address,
|
|
hw_buffer,
|
|
split_munch2,
|
|
size
|
|
);
|
|
}
|
|
else
|
|
{
|
|
/* no wrap - do the transfer */
|
|
do_transfer
|
|
(
|
|
dcp->mode[chan].bits.transfer_type,
|
|
dcp->mode[chan].bits.address_dec,
|
|
address,
|
|
hw_buffer,
|
|
munch,
|
|
size
|
|
);
|
|
}
|
|
|
|
/* get the final offset and count */
|
|
offset -= munch;
|
|
count -= munch;
|
|
}
|
|
|
|
/* restore the DMA offset and count from the working copies */
|
|
dcp->current_address[chan][1] = offset >> 8;
|
|
dcp->current_address[chan][0] = offset;
|
|
dcp->current_count[chan][1] = count >> 8;
|
|
dcp->current_count[chan][0] = count;
|
|
|
|
if (count == 0xffff)
|
|
{
|
|
/*
|
|
* Terminal count has been reached
|
|
*/
|
|
|
|
/* no more transfers are required */
|
|
result = FALSE;
|
|
|
|
/* update the status register */
|
|
dcp->status.bits.terminal_count |= (1 << chan);
|
|
dcp->status.bits.request &= ~(1 << chan);
|
|
|
|
/* if autoinitialization is enabled, then reset
|
|
the channel and wait for a new request */
|
|
if (dcp->mode[chan].bits.auto_init != 0)
|
|
{
|
|
dcp->current_count[chan][0] =
|
|
dcp->base_count[chan][0];
|
|
dcp->current_count[chan][1] =
|
|
dcp->base_count[chan][1];
|
|
|
|
dcp->current_count[chan][0] =
|
|
dcp->base_count[chan][0];
|
|
dcp->current_count[chan][1] =
|
|
dcp->base_count[chan][1];
|
|
}
|
|
else
|
|
{
|
|
/* set the mask bit for the channel */
|
|
dcp->mask |= (1 << chan);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
GLOBAL void dma_enquire IFN3(half_word, channel,
|
|
sys_addr *, address, word *, length)
|
|
{
|
|
register DMA_CNTRL *dcp;
|
|
register unsigned int chan;
|
|
|
|
note_trace0_no_nl(DMA_VERBOSE, "dma_enquire() ");
|
|
|
|
/* get a pointer to the controller and the physical channel
|
|
number */
|
|
dcp = &adaptor.controller[dma_physical_controller(channel)];
|
|
chan = dma_physical_channel(channel);
|
|
|
|
/* build the address */
|
|
*address = dma_system_address(channel,
|
|
adaptor.pages.page[channel],
|
|
( ( (unsigned int)dcp->current_address[chan][1] << 8)
|
|
| ( dcp->current_address[chan][0] << 0) ) );
|
|
|
|
/* build the count */
|
|
*length = ( ((unsigned int)dcp->current_count[chan][1] << 8)
|
|
| (dcp->current_count[chan][0] << 0) );
|
|
|
|
note_trace3(DMA_VERBOSE, " channel %d, returning 0x%08x+%04x",
|
|
channel, *address, *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(void)
|
|
{
|
|
return &adaptor;
|
|
}
|
|
#endif /* NTVDM */
|
|
|
|
|
|
/*
|
|
* ============================================================================
|
|
* Internal functions
|
|
* ============================================================================
|
|
*/
|
|
|
|
LOCAL void do_transfer IFN6(int, type, int, decrement,
|
|
sys_addr, dma_addr, char *, hw_buffer, unsigned long, length,
|
|
unsigned long, size)
|
|
{
|
|
/*
|
|
* This function moves the data for a DMA transfer.
|
|
*
|
|
* The value of "type" may be:
|
|
* DMA_WRITE_TRANSFER - data is moved from the I/O
|
|
* device's memory space to system address space;
|
|
* DMA_READ_TRANSFER - data is moved from system
|
|
* address space to the I/O device's memory space
|
|
* DMA_VERIFY_TRANSFER - no data is required to be
|
|
* moved at all.
|
|
*
|
|
* If "decrement" is TRUE, the pointer to system address
|
|
* space is decremented during the DMA transfer; otherwise
|
|
* the pointer is incremented. The pointer to the I/O
|
|
* device's memory space is always incremented.
|
|
*
|
|
* "dma_addr" is the offset in system address space where
|
|
* the DMA transfer will start; "hw_buffer" is the address
|
|
* of the buffer in the I/O device's memory space where
|
|
* the DMA transfer will start.
|
|
*
|
|
* "length" is the number of units of "size" bytes each
|
|
* that must be transferred.
|
|
*/
|
|
|
|
/* convert the length to bytes */
|
|
length *= size;
|
|
|
|
/* do the transfer */
|
|
switch(type)
|
|
{
|
|
case DMA_WRITE_TRANSFER:
|
|
if (!decrement)
|
|
{
|
|
#ifndef PM
|
|
#ifdef LIM
|
|
if( !get_lim_setup() ){
|
|
if( dma_addr >= ROM_START ){
|
|
if( dma_addr >= ROM_START )
|
|
break;
|
|
length = ROM_START - dma_addr - 1;
|
|
}
|
|
}
|
|
#else
|
|
/* increment case - check for writing to ROM */
|
|
if ((dma_addr + length) >= ROM_START)
|
|
{
|
|
if (dma_addr >= ROM_START)
|
|
break;
|
|
length = ROM_START - dma_addr - 1;
|
|
}
|
|
#endif /* LIM */
|
|
#endif /* nPM */
|
|
sas_PWS(dma_addr, (host_addr) hw_buffer, length);
|
|
|
|
}
|
|
else
|
|
{
|
|
/* decrement case - check for writing to ROM */
|
|
#ifndef PM
|
|
#ifdef LIM
|
|
if( !get_lim_setup() ){
|
|
if (dma_addr >= ROM_START) {
|
|
if (dma_addr-length >= ROM_START)
|
|
break;
|
|
length = dma_addr-ROM_START+length-1;
|
|
dma_addr = ROM_START - 1;
|
|
}
|
|
}
|
|
#else
|
|
if (dma_addr >= ROM_START)
|
|
{
|
|
if (dma_addr-length >= ROM_START)
|
|
break;
|
|
length = dma_addr - ROM_START + length - 1;
|
|
dma_addr = ROM_START - 1;
|
|
}
|
|
#endif /* LIM */
|
|
#endif /* nPM */
|
|
bwd_dest_copy_to_M((half_word *)hw_buffer, dma_addr, length);
|
|
|
|
}
|
|
break;
|
|
|
|
case DMA_READ_TRANSFER:
|
|
if (!decrement)
|
|
/* increment case */
|
|
sas_PRS(dma_addr, (host_addr) hw_buffer, length);
|
|
else
|
|
/* decrement case */
|
|
bwd_dest_copy_from_M(dma_addr, (half_word *)hw_buffer, length);
|
|
break;
|
|
|
|
case DMA_VERIFY_TRANSFER:
|
|
break;
|
|
|
|
default:
|
|
note_trace0(DMA_VERBOSE, "dma_request() illegal transfer");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* backward copy routines - these used to be
|
|
* in the host but there seemed to be little point
|
|
*/
|
|
|
|
LOCAL void bwd_dest_copy_to_M IFN3(host_addr, s, sys_addr, d,
|
|
sys_addr, l)
|
|
{
|
|
while (l-- > 0)
|
|
sas_PW8(d--, *s++);
|
|
}
|
|
|
|
LOCAL void bwd_dest_copy_from_M IFN3(sys_addr, s, host_addr, d,
|
|
sys_addr, l)
|
|
{
|
|
while (l-- > 0)
|
|
*d-- = sas_PR8(s++);
|
|
}
|
|
|
|
#ifdef SEGMENTATION
|
|
/*
|
|
* The following #include specifies the code segment into which this
|
|
* module will by placed by the MPW C compiler on the Mac II running
|
|
* MultiFinder.
|
|
*/
|
|
#include "SOFTPC_INIT.seg"
|
|
#endif
|
|
|
|
GLOBAL void dma_init IFN0()
|
|
{
|
|
io_addr port;
|
|
|
|
note_trace0(DMA_VERBOSE, "dma_init() called");
|
|
|
|
#ifdef LIM
|
|
lim_active = 0;
|
|
#endif
|
|
|
|
/*
|
|
* Connect the DMA Adaptor chips to the I/O bus
|
|
*/
|
|
|
|
/* establish the DMA Controller I/O functions that will be used */
|
|
io_define_inb(DMA_ADAPTOR, dma_inb);
|
|
io_define_outb(DMA_ADAPTOR, dma_outb);
|
|
|
|
/* connect the DMA Controller chips to the I/O bus */
|
|
for (port = DMA_PORT_START; port <= DMA_PORT_END; port++)
|
|
io_connect_port(port, DMA_ADAPTOR, IO_READ_WRITE);
|
|
for (port = DMA1_PORT_START; port <= DMA1_PORT_END; port++)
|
|
io_connect_port(port, DMA_ADAPTOR, IO_READ_WRITE);
|
|
|
|
/* establish the DMA Page Register I/O functions that will be used */
|
|
io_define_inb(DMA_PAGE_ADAPTOR, dma_page_inb);
|
|
io_define_outb(DMA_PAGE_ADAPTOR, dma_page_outb);
|
|
|
|
/* connect the DMA Page Register chip to the I/O bus */
|
|
for (port = DMA_PAGE_PORT_START; port <= DMA_PAGE_PORT_END; port++)
|
|
io_connect_port(port, DMA_PAGE_ADAPTOR, IO_READ_WRITE);
|
|
}
|