/* 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); }