source: mainline/uspace/drv/bus/isa/dma_controller.c@ 85c4cc45

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 85c4cc45 was a066c1b9, checked in by Jan Vesely <jano.vesely@…>, 14 years ago

isa: Drop double newlines.

  • Property mode set to 100644
File size: 10.6 KB
Line 
1/*
2 * Copyright (c) 2011 Jan Vesely
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28/** @addtogroup isa
29 * @{
30 */
31/** @file
32 * @brief DMA controller management
33 */
34#include <assert.h>
35#include <bool.h>
36#include <errno.h>
37#include <ddi.h> /* pio_enable */
38#include <libarch/ddi.h> /* pio_write */
39#include <ddf/log.h>
40
41#include "dma_controller.h"
42
43/** DMA Slave controller I/O Address. */
44#define DMA_CONTROLLER_FIRST_BASE ((void*)0x0)
45typedef struct dma_controller_regs_first {
46 uint8_t channel_start0;
47 uint8_t channel_count0;
48 uint8_t channel_start1;
49 uint8_t channel_count1;
50 uint8_t channel_start2;
51 uint8_t channel_count2;
52 uint8_t channel_start3;
53 uint8_t channel_count3;
54
55 uint8_t command_status;
56#define DMA_STATUS_REQ(x) (1 << ((x % 4) + 4))
57#define DMA_STATUS_COMPLETE(x) (1 << (x % 4))
58/* http://wiki.osdev.org/DMA: The only bit that works is COND(bit 2) */
59#define DMA_COMMAND_COND (1 << 2) /* Disables DMA controller */
60
61 uint8_t request; /* Memory to memory transfers, NOT implemented on PCs*/
62 uint8_t single_mask;
63#define DMA_SINGLE_MASK_CHAN_SEL_MASK (0x3)
64#define DMA_SINGLE_MASK_CHAN_SEL_SHIFT (0)
65#define DMA_SINGLE_MASK_CHAN_TO_REG(x) \
66 ((x & DMA_SINGLE_MASK_CHAN_SEL_MASK) << DMA_SINGLE_MASK_CHAN_SEL_SHIFT)
67#define DMA_SINGLE_MASK_MASKED_FLAG (1 << 2)
68
69 uint8_t mode;
70#define DMA_MODE_CHAN_SELECT_MASK (0x3)
71#define DMA_MODE_CHAN_SELECT_SHIFT (0)
72#define DMA_MODE_CHAN_TO_REG(x) \
73 ((x & DMA_MODE_CHAN_SELECT_MASK) << DMA_MODE_CHAN_SELECT_SHIFT)
74#define DMA_MODE_CHAN_TRA_MASK (0x3)
75#define DMA_MODE_CHAN_TRA_SHIFT (2)
76#define DMA_MODE_CHAN_TRA_SELF_TEST (0)
77#define DMA_MODE_CHAN_TRA_WRITE (0x1)
78#define DMA_MODE_CHAN_TRA_READ (0x2)
79#define DMA_MODE_CHAN_AUTO_FLAG (1 << 4)
80#define DMA_MODE_CHAN_DOWN_FLAG (1 << 5)
81#define DMA_MODE_CHAN_MODE_MASK (0x3)
82#define DMA_MODE_CHAN_MODE_SHIFT (6)
83#define DMA_MODE_CHAN_MODE_DEMAND (0)
84#define DMA_MODE_CHAN_MODE_SINGLE (1)
85#define DMA_MODE_CHAN_MODE_BLOCK (2)
86#define DMA_MODE_CHAN_MODE_CASCADE (3)
87
88 uint8_t flip_flop;
89 /* Master reset sets Flip-Flop low, clears status,
90 * sets all mask bits on */
91 uint8_t master_reset; /* Intermediate is not implemented on PCs */
92 uint8_t mask_reset;
93
94 uint8_t multi_mask;
95#define DMA_MULTI_MASK_CHAN(x) (1 << (x % 4))
96
97} dma_controller_regs_first_t;
98
99/** DMA Master controller I/O Address. */
100#define DMA_CONTROLLER_SECOND_BASE ((void*)0xc0)
101/* See dma_controller_regs_first_t for register values */
102typedef struct dma_controller_regs_second {
103 uint8_t channel_start4;
104 uint8_t reserved0;
105 uint8_t channel_count4;
106 uint8_t reserved1;
107 uint8_t channel_start5;
108 uint8_t reserved2;
109 uint8_t channel_count5;
110 uint8_t reserved3;
111 uint8_t channel_start6;
112 uint8_t reserved4;
113 uint8_t channel_count6;
114 uint8_t reserved5;
115 uint8_t channel_start7;
116 uint8_t reserved6;
117 uint8_t channel_count7;
118
119 uint8_t command_status;
120 uint8_t reserved8;
121 uint8_t request;
122 uint8_t reserved9;
123 uint8_t single_mask;
124 uint8_t reserveda;
125 uint8_t mode;
126 uint8_t reservedb;
127 uint8_t flip_flop;
128 uint8_t reservedc;
129 uint8_t master_reset;
130 uint8_t reservedd;
131 uint8_t multi_mask;
132} dma_controller_regs_second_t;
133
134/** Shared DMA page address register I/O address. */
135#define DMA_CONTROLLER_PAGE_BASE ((void*)0x81)
136typedef struct dma_page_regs {
137 uint8_t channel2;
138 uint8_t channel3;
139 uint8_t channel1;
140 uint8_t reserved0;
141 uint8_t reserved1;
142 uint8_t reserved2;
143 uint8_t channel0;
144 uint8_t reserved3;
145 uint8_t channel6;
146 uint8_t channel7;
147 uint8_t channel5;
148 uint8_t reserved4;
149 uint8_t reserved5;
150 uint8_t reserved6;
151 uint8_t channel4;
152} dma_page_regs_t;
153
154/** Addresses needed to setup a DMA channel. */
155typedef struct dma_channel {
156 ioport8_t *offset_reg_address;
157 ioport8_t *size_reg_address;
158 ioport8_t *page_reg_address;
159 ioport8_t *single_mask_address;
160 ioport8_t *mode_address;
161 ioport8_t *flip_flop_address;
162} dma_channel_t;
163
164typedef struct dma_controller {
165 dma_channel_t channels[8];
166 dma_page_regs_t *page_table;
167 dma_controller_regs_first_t *first;
168 dma_controller_regs_second_t *second;
169 bool initialized;
170} dma_controller_t;
171
172
173/** Standard i8237 DMA controller.
174 * http://zet.aluzina.org/index.php/8237_DMA_controller#DMA_Channel_Registers
175 */
176static dma_controller_t controller_8237 = {
177 .channels = {
178 /* The first chip 8-bit */
179 { (uint8_t*)0x00, (uint8_t*)0x01, (uint8_t*)0x87,
180 (uint8_t*)0x0a, (uint8_t*)0x0b, (uint8_t*)0x0c, },
181 { (uint8_t*)0x02, (uint8_t*)0x03, (uint8_t*)0x83,
182 (uint8_t*)0x0a, (uint8_t*)0x0b, (uint8_t*)0x0c, },
183 { (uint8_t*)0x04, (uint8_t*)0x05, (uint8_t*)0x81,
184 (uint8_t*)0x0a, (uint8_t*)0x0b, (uint8_t*)0x0c, },
185 { (uint8_t*)0x06, (uint8_t*)0x07, (uint8_t*)0x82,
186 (uint8_t*)0x0a, (uint8_t*)0x0b, (uint8_t*)0x0c, },
187
188 /* The second chip 16-bit */
189 { (uint8_t*)0xc0, (uint8_t*)0xc2, (uint8_t*)0x8f,
190 (uint8_t*)0xd4, (uint8_t*)0xd6, (uint8_t*)0xd8, },
191 { (uint8_t*)0xc4, (uint8_t*)0xc6, (uint8_t*)0x8b,
192 (uint8_t*)0xd4, (uint8_t*)0xd6, (uint8_t*)0xd8, },
193 { (uint8_t*)0xc8, (uint8_t*)0xca, (uint8_t*)0x89,
194 (uint8_t*)0xd4, (uint8_t*)0xd6, (uint8_t*)0xd8, },
195 { (uint8_t*)0xcc, (uint8_t*)0xce, (uint8_t*)0x8a,
196 (uint8_t*)0xd4, (uint8_t*)0xd6, (uint8_t*)0xd8, }, },
197
198 .page_table = NULL,
199 .first = NULL,
200 .second = NULL,
201 .initialized = false,
202};
203
204/* Initialize I/O access to DMA controller I/O ports.
205 * param controller DMA Controller structure to initialize.
206 * return Error code.
207 */
208static inline int dma_controller_init(dma_controller_t *controller)
209{
210 assert(controller);
211 int ret = pio_enable(
212 DMA_CONTROLLER_PAGE_BASE, sizeof(dma_page_regs_t),
213 (void**)&controller->page_table);
214 if (ret != EOK)
215 return EIO;
216
217 ret = pio_enable(
218 DMA_CONTROLLER_FIRST_BASE, sizeof(dma_controller_regs_first_t),
219 (void**)&controller->first);
220 if (ret != EOK)
221 return EIO;
222
223 ret = pio_enable(
224 DMA_CONTROLLER_SECOND_BASE, sizeof(dma_controller_regs_second_t),
225 (void**)&controller->second);
226 if (ret != EOK)
227 return EIO;
228 controller->initialized = true;
229
230 /* Reset the controller */
231 pio_write_8(&controller->second->master_reset, 0xff);
232 pio_write_8(&controller->first->master_reset, 0xff);
233
234
235 return EOK;
236}
237/*----------------------------------------------------------------------------*/
238/**
239 * Setup DMA channel to specified place and mode.
240 * @param channel DMA Channel 1,2,3 for 8 bit transfers, 5,6,7 for 16 bit.
241 * @param pa Physical address of the buffer. Must be < 16MB for 16 bit and < 1MB
242 * for 8 bit transfers.
243 * @param size DMA buffer size, limited to 64K.
244 * @param mode Mode of the DMA channel:
245 * - Read or Write
246 * - Allow automatic reset
247 * - Use address decrement instead of increment
248 * - Use SINGLE/BLOCK/ON DEMAND transfer mode
249 * @return Error code.
250 */
251int dma_setup_channel(
252 unsigned channel, uint32_t pa, uint16_t size, uint8_t mode)
253{
254 if (channel == 0 || channel == 4)
255 return ENOTSUP;
256 if (channel > 7)
257 return ENOENT;
258
259 /* DMA is limited to 24 but addresses. */
260 if (pa >= (1 << 24))
261 return EINVAL;
262
263 /* 8 bit channels use only 4 bits from the page register. */
264 if (channel > 0 && channel < 4 && pa >= (1 << 20))
265 return EINVAL;
266
267 if (!controller_8237.initialized)
268 dma_controller_init(&controller_8237);
269
270 if (!controller_8237.initialized)
271 return EIO;
272
273 /* 16 bit transfers are a bit special */
274 ddf_msg(LVL_DEBUG, "Unspoiled address: %p and size: %zu.", pa, size);
275 if (channel > 4) {
276 /* Size must be aligned to 16 bits */
277 if ((size & 1) != 0)
278 return EINVAL;
279 size >>= 1;
280 /* Address is fun: lower 16bits need to be shifted by 1 */
281 pa = ((pa & 0xffff) >> 1) | (pa & 0xff0000);
282 }
283
284 const dma_channel_t dma_channel = controller_8237.channels[channel];
285
286 ddf_msg(LVL_DEBUG,
287 "Setting channel %u, to address %p(%zu), mode %hhx.",
288 channel, pa, size, mode);
289
290 /* Mask DMA request */
291 uint8_t value = DMA_SINGLE_MASK_CHAN_TO_REG(channel)
292 | DMA_SINGLE_MASK_MASKED_FLAG;
293 pio_write_8(dma_channel.single_mask_address, value);
294
295 /* Set mode */
296 value = DMA_MODE_CHAN_TO_REG(channel) | mode;
297 ddf_msg(LVL_DEBUG2, "Writing mode byte: %p:%hhx.",
298 dma_channel.mode_address, value);
299 pio_write_8(dma_channel.mode_address, value);
300
301 /* Set address -- reset flip-flop*/
302 pio_write_8(dma_channel.flip_flop_address, 0);
303
304 /* Low byte */
305 value = pa & 0xff;
306 ddf_msg(LVL_DEBUG2, "Writing address low byte: %p:%hhx.",
307 dma_channel.offset_reg_address, value);
308 pio_write_8(dma_channel.offset_reg_address, value);
309
310 /* High byte */
311 value = (pa >> 8) & 0xff;
312 ddf_msg(LVL_DEBUG2, "Writing address high byte: %p:%hhx.",
313 dma_channel.offset_reg_address, value);
314 pio_write_8(dma_channel.offset_reg_address, value);
315
316 /* Page address - third byte */
317 value = (pa >> 16) & 0xff;
318 ddf_msg(LVL_DEBUG2, "Writing address page byte: %p:%hhx.",
319 dma_channel.page_reg_address, value);
320 pio_write_8(dma_channel.page_reg_address, value);
321
322 /* Set size -- reset flip-flop */
323 pio_write_8(dma_channel.flip_flop_address, 0);
324
325 /* Low byte */
326 value = (size - 1) & 0xff;
327 ddf_msg(LVL_DEBUG2, "Writing size low byte: %p:%hhx.",
328 dma_channel.size_reg_address, value);
329 pio_write_8(dma_channel.size_reg_address, value);
330
331 /* High byte */
332 value = ((size - 1) >> 8) & 0xff;
333 ddf_msg(LVL_DEBUG2, "Writing size high byte: %p:%hhx.",
334 dma_channel.size_reg_address, value);
335 pio_write_8(dma_channel.size_reg_address, value);
336
337 /* Unmask DMA request */
338 value = DMA_SINGLE_MASK_CHAN_TO_REG(channel);
339 pio_write_8(dma_channel.single_mask_address, value);
340
341 return EOK;
342}
343/**
344 * @}
345 */
Note: See TracBrowser for help on using the repository browser.