#include <panic.h>
#include <asm.h>
static const unsigned short address_port[] = {0x0, 0x2, 0x4, 0x6};
static const unsigned short word_count_port[] = {0x1, 0x3, 0x5, 0x7};
static const unsigned short page_port[] = {0, 0x83, 0x81, 0x82};
static inline void disable_channel(unsigned char channel)
{
outbp(0x04 | channel, 0xa);
}
static inline void enable_channel(unsigned char channel)
{
outbp(channel, 0xa);
}
static inline void reset()
{
outbp(0, 0xc);
}
static inline void set_mode(unsigned char channel, unsigned char transfer_type)
{
outbp(0x40 | (transfer_type << 2) | channel, 0xb);
}
static inline void set_address(unsigned char channel, unsigned long address)
{
register unsigned short port;
if (channel != 0)
outbp(address >> 16, page_port[channel]);
port = address_port[channel];
outbp(address, port);
outbp(address >> 8, port);
}
static inline void set_word_count(unsigned char channel, unsigned short word_count)
{
register unsigned short port;
port = word_count_port[channel];
outbp(word_count, port);
outbp(word_count >> 8, port);
}
static void setup_dma(unsigned char channel, unsigned char transfer_type, unsigned long address, unsigned int count)
{
cli();
disable_channel(channel);
reset();
set_mode(channel, transfer_type);
set_address(channel, address);
set_word_count(channel, count - 1);
enable_channel(channel);
sti();
}
void dma_setup(unsigned char channel, unsigned char transfer_type, unsigned long address, unsigned int count)
{
if (address > 0x100000)
panic("DMA: region must be in the first 1MB of memory");
if (count > 0x10000)
panic("DMA: cannot transfer more than 64K");
if ((address + count) >> 16 != address >> 16)
panic("DMA: region crosses a physical alignment boundary");
setup_dma(channel, transfer_type, address, count);
}