census logo
[bootcensus] / src / dma.c
1 /*
2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include "dma.h"
19 #include "asmops.h"
20
21 /* 8bit DMA ports */
22 #define DMA_0_ADDR      0x00
23 #define DMA_0_COUNT     0x01
24 #define DMA_1_ADDR      0x02
25 #define DMA_1_COUNT     0x03
26 #define DMA_2_ADDR      0x04
27 #define DMA_2_COUNT     0x05
28 #define DMA_3_ADDR      0x06
29 #define DMA_3_COUNT     0x07
30 /* 16bit DMA ports */
31 #define DMA_4_ADDR      0xc0
32 #define DMA_4_COUNT     0xc2
33 #define DMA_5_ADDR      0xc4
34 #define DMA_5_COUNT     0xc6
35 #define DMA_6_ADDR      0xc8
36 #define DMA_6_COUNT     0xca
37 #define DMA_7_ADDR      0xcc
38 #define DMA_7_COUNT     0xce
39
40 #define DMA_ADDR(c)     \
41         ((c < 4) ? DMA_0_ADDR + ((c) << 1) : (DMA_4_ADDR + ((c) << 2)))
42 #define DMA_COUNT(c) \
43         ((c < 4) ? DMA_0_COUNT + ((c) << 1) : (DMA_4_COUNT + ((c) << 2)))
44
45 #define DMA8_MASK                       0x0a
46 #define DMA8_MODE                       0x0b
47 #define DMA8_CLR_FLIPFLOP       0x0c
48 #define DMA8_RESET                      0x0d
49 #define DMA8_MASK_RST           0x0e
50 #define DMA8_RMASK                      0x0f
51 #define DMA16_MASK                      0xd4
52 #define DMA16_MODE                      0xd6
53 #define DMA16_CLR_FLIPFLOP      0xd8
54 #define DMA16_RESET                     0xda
55 #define DMA16_MASK_RST          0xdc
56 #define DMA16_RMASK             0xde
57
58 #define DMA_MASK(c)     ((c) < 4 ? DMA8_MASK : DMA16_MASK)
59 #define DMA_MODE(c)     ((c) < 4 ? DMA8_MODE : DMA16_MODE)
60 #define DMA_CLR_FLIPFLOP(c)     ((c) < 4 ? DMA8_CLR_FLIPFLOP : DMA16_CLR_FLIPFLOP)
61 #define DMA_RESET(c)    ((c) < 4 ? DMA8_RESET : DMA16_RESET)
62 #define DMA_MASK_RST(c) ((c) < 4 ? DMA8_MASK_RST : DMA16_MASK_RST)
63 #define DMA_RMASK(c)    ((c) < 4 ? DMA8_RMASK : DMA16_RMASK)
64
65 #define DMA_0_PAGE              0x87
66 #define DMA_1_PAGE              0x83
67 #define DMA_2_PAGE              0x81
68 #define DMA_3_PAGE              0x82
69 #define DMA_4_PAGE              0x8f
70 #define DMA_5_PAGE              0x8b
71 #define DMA_6_PAGE              0x89
72 #define DMA_7_PAGE              0x8a
73
74 #define MODE_CHAN(x)    ((x) & 3)
75 #define MODE_WRITE              0x04
76 #define MODE_READ               0x08
77 #define MODE_AUTO               0x10
78 #define MODE_DECR               0x20
79 #define MODE_SINGLE             0x40
80 #define MODE_BLOCK              0x80
81 #define MODE_CASCADE    0xc0
82
83 #define MASK_CHAN(x)    ((x) & 3)
84 #define MASK_DISABLE    0x04
85
86 #define RMASK_CHAN(x)   (1 << ((x) & 3))
87
88 #define IS_16BIT(c)     ((c) >= 4)
89
90 static void dma_io(int chan, uint32_t phyaddr, int size, unsigned int flags, unsigned int dir);
91 static inline void mask(int chan);
92 static inline void unmask(int chan);
93
94 static int page_port[] = {
95         DMA_0_PAGE, DMA_1_PAGE, DMA_2_PAGE, DMA_3_PAGE,
96         DMA_4_PAGE, DMA_5_PAGE, DMA_6_PAGE, DMA_7_PAGE
97 };
98
99 void dma_out(int chan, uint32_t phyaddr, int size, unsigned int flags)
100 {
101         dma_io(chan, phyaddr, size, flags, MODE_READ);
102 }
103
104 void dma_in(int chan, uint32_t phyaddr, int size, unsigned int flags)
105 {
106         dma_io(chan, phyaddr, size, flags, MODE_WRITE);
107 }
108
109 static void dma_io(int chan, uint32_t phyaddr, int size, unsigned int flags, unsigned int dir)
110 {
111         unsigned int mode;
112         int addr_port, count_port;
113
114         addr_port = DMA_ADDR(chan);
115         count_port = DMA_COUNT(chan);
116
117         mask(chan);
118         outb(0, DMA_CLR_FLIPFLOP(chan));
119
120         /* first 2 bits of flags correspond to the mode bits 6,7 */
121         mode = ((flags & 3) << 6) | dir | MODE_CHAN(chan);
122         if(flags & DMA_DECR) mode |= MODE_DECR;
123         if(flags & DMA_AUTO) mode |= MODE_AUTO;
124         outb(mode, DMA_MODE(chan));
125
126         if(IS_16BIT(chan)) {
127                 phyaddr >>= 1;
128                 size >>= 1;
129         }
130
131         outb(phyaddr & 0xff, addr_port);
132         outb((phyaddr >> 8) & 0xff, addr_port);
133         outb((phyaddr >> 16) & 0xff, page_port[chan]);
134
135         size--;
136         outb(size & 0xff, count_port);
137         outb((size >> 8) & 0xff, count_port);
138
139         unmask(chan);
140 }
141
142 static inline void mask(int chan)
143 {
144         outb(MASK_CHAN(chan) | MASK_DISABLE, DMA_MASK(chan));
145 }
146
147 static inline void unmask(int chan)
148 {
149         outb(MASK_CHAN(chan), DMA_MASK(chan));
150 }