/* Pilote pour le controleur DMA Intel 8237A */
/* Copyright (C) 2008 Emmanuel Varoquaux
This file is part of XOS.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
/* Le pilote est actuellement limite aux canaux 0 a 3. Il serait facile de
* l'etendre aux canaux 4 a 7. */
#include
#include
/* registres du controleur */
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); /* clear the flip-flop */
}
static inline void set_mode(unsigned char channel, unsigned char transfer_type)
{
outbp(0x40 | (transfer_type << 2) | channel, 0xb); /* disable auto-initialization, address decrement, single mode */
}
static inline void set_address(unsigned char channel, unsigned long address)
{
register unsigned short port;
if (channel != 0)
outbp(address >> 16, page_port[channel]);/* page (bits 16-19) */
port = address_port[channel];
outbp(address, port); /* bits 0-7 */
outbp(address >> 8, port); /* bits 8-15 */
}
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); /* 8 bits de poids faible */
outbp(word_count >> 8, port); /* 8 bits de poids fort */
}
static void setup_dma(unsigned char channel, unsigned char transfer_type, unsigned long address, unsigned int count)
{
cli();
disable_channel(channel); /* on masque le canal */
reset(); /* reset */
set_mode(channel, transfer_type); /* commande */
set_address(channel, address); /* adresse */
set_word_count(channel, count - 1); /* nombre de transferts */
enable_channel(channel); /* on active le canal */
sti();
}
/* address et count doivent etre tels que :
* - count doit etre inferieur ou egal a 64K
* - la zone memoire ne doit pas traverser une frontiere de page (64K). */
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);
}