source: mainline/uspace/drv/bus/isa/i8237.c@ b28dabe

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

isa: Implement DMA remain size query.

  • Property mode set to 100644
File size: 13.1 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
29/** @addtogroup isa
30 * @{
31 */
32
33/** @file
34 * @brief DMA controller management
35 */
36
37#include <assert.h>
38#include <bool.h>
39#include <errno.h>
40#include <fibril_synch.h>
41#include <ddi.h>
42#include <libarch/ddi.h>
43#include <ddf/log.h>
44#include "i8237.h"
45
46/** DMA Slave controller I/O Address. */
47#define DMA_CONTROLLER_FIRST_BASE ((void *) 0x00)
48
49/** DMA Master controller I/O Address. */
50#define DMA_CONTROLLER_SECOND_BASE ((void *) 0xc0)
51
52/** Shared DMA page address register I/O address. */
53#define DMA_CONTROLLER_PAGE_BASE ((void *) 0x81)
54
55#define DMA_STATUS_REQ(x) (1 << (((x) % 4) + 4))
56#define DMA_STATUS_COMPLETE(x) (1 << ((x) % 4))
57
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#define DMA_SINGLE_MASK_CHAN_SEL_MASK 0x03
62#define DMA_SINGLE_MASK_CHAN_SEL_SHIFT 0
63
64#define DMA_SINGLE_MASK_CHAN_TO_REG(x) \
65 (((x) & DMA_SINGLE_MASK_CHAN_SEL_MASK) << DMA_SINGLE_MASK_CHAN_SEL_SHIFT)
66
67#define DMA_SINGLE_MASK_MASKED_FLAG (1 << 2)
68
69#define DMA_MODE_CHAN_SELECT_MASK 0x03
70#define DMA_MODE_CHAN_SELECT_SHIFT 0
71
72#define DMA_MODE_CHAN_TO_REG(x) \
73 (((x) & DMA_MODE_CHAN_SELECT_MASK) << DMA_MODE_CHAN_SELECT_SHIFT)
74
75#define DMA_MODE_CHAN_TRA_MASK 0x03
76#define DMA_MODE_CHAN_TRA_SHIFT 2
77#define DMA_MODE_CHAN_TRA_SELF_TEST 0
78#define DMA_MODE_CHAN_TRA_WRITE 0x01
79#define DMA_MODE_CHAN_TRA_READ 0x02
80
81#define DMA_MODE_CHAN_AUTO_FLAG (1 << 4)
82#define DMA_MODE_CHAN_DOWN_FLAG (1 << 5)
83
84#define DMA_MODE_CHAN_MODE_MASK 0x03
85#define DMA_MODE_CHAN_MODE_SHIFT 6
86#define DMA_MODE_CHAN_MODE_DEMAND 0
87#define DMA_MODE_CHAN_MODE_SINGLE 1
88#define DMA_MODE_CHAN_MODE_BLOCK 2
89#define DMA_MODE_CHAN_MODE_CASCADE 3
90
91#define DMA_MULTI_MASK_CHAN(x) (1 << ((x) % 4))
92
93typedef struct {
94 uint8_t channel_start0;
95 uint8_t channel_count0;
96 uint8_t channel_start1;
97 uint8_t channel_count1;
98 uint8_t channel_start2;
99 uint8_t channel_count2;
100 uint8_t channel_start3;
101 uint8_t channel_count3;
102
103 uint8_t command_status;
104
105 /** Memory to memory transfers, NOT implemented on PCs */
106 uint8_t request;
107 uint8_t single_mask;
108 uint8_t mode;
109 uint8_t flip_flop;
110
111 /*
112 * Master reset sets Flip-Flop low, clears status,
113 * sets all mask bits on.
114 *
115 * Intermediate is not implemented on PCs.
116 *
117 */
118 uint8_t master_reset;
119 uint8_t mask_reset;
120 uint8_t multi_mask;
121} dma_controller_regs_first_t;
122
123typedef struct {
124 uint8_t channel_start4;
125 uint8_t reserved0;
126 uint8_t channel_count4;
127 uint8_t reserved1;
128 uint8_t channel_start5;
129 uint8_t reserved2;
130 uint8_t channel_count5;
131 uint8_t reserved3;
132 uint8_t channel_start6;
133 uint8_t reserved4;
134 uint8_t channel_count6;
135 uint8_t reserved5;
136 uint8_t channel_start7;
137 uint8_t reserved6;
138 uint8_t channel_count7;
139
140 uint8_t command_status;
141 uint8_t reserved8;
142 uint8_t request;
143 uint8_t reserved9;
144 uint8_t single_mask;
145 uint8_t reserveda;
146 uint8_t mode;
147 uint8_t reservedb;
148 uint8_t flip_flop;
149 uint8_t reservedc;
150 uint8_t master_reset;
151 uint8_t reservedd;
152 uint8_t multi_mask;
153} dma_controller_regs_second_t;
154
155typedef struct {
156 uint8_t channel2;
157 uint8_t channel3;
158 uint8_t channel1;
159 uint8_t reserved0;
160 uint8_t reserved1;
161 uint8_t reserved2;
162 uint8_t channel0;
163 uint8_t reserved3;
164 uint8_t channel6;
165 uint8_t channel7;
166 uint8_t channel5;
167 uint8_t reserved4;
168 uint8_t reserved5;
169 uint8_t reserved6;
170 uint8_t channel4;
171} dma_page_regs_t;
172
173/** Addresses needed to setup a DMA channel. */
174typedef struct {
175 ioport8_t *offset_reg_address;
176 ioport8_t *size_reg_address;
177 ioport8_t *page_reg_address;
178 ioport8_t *single_mask_address;
179 ioport8_t *mode_address;
180 ioport8_t *flip_flop_address;
181} dma_channel_t;
182
183typedef struct {
184 dma_channel_t channels[8];
185 dma_page_regs_t *page_table;
186 dma_controller_regs_first_t *first;
187 dma_controller_regs_second_t *second;
188 bool initialized;
189} dma_controller_t;
190
191static fibril_mutex_t guard = FIBRIL_MUTEX_INITIALIZER(guard);
192
193/** Standard i8237 DMA controller.
194 *
195 * http://zet.aluzina.org/index.php/8237_DMA_controller#DMA_Channel_Registers
196 *
197 */
198static dma_controller_t controller_8237 = {
199 .channels = {
200 /* The first chip 8-bit */
201 { /* Channel 0 - Unusable*/
202 .offset_reg_address = (uint8_t *) 0x00,
203 .size_reg_address = (uint8_t *) 0x01,
204 .page_reg_address = (uint8_t *) 0x87,
205 .single_mask_address = (uint8_t *) 0x0a,
206 .mode_address = (uint8_t *) 0x0b,
207 .flip_flop_address = (uint8_t *) 0x0c,
208 },
209 { /* Channel 1 */
210 .offset_reg_address = (uint8_t *) 0x02,
211 .size_reg_address = (uint8_t *) 0x03,
212 .page_reg_address = (uint8_t *) 0x83,
213 .single_mask_address = (uint8_t *) 0x0a,
214 .mode_address = (uint8_t *) 0x0b,
215 .flip_flop_address = (uint8_t *) 0x0c,
216 },
217 { /* Channel 2 */
218 .offset_reg_address = (uint8_t *) 0x04,
219 .size_reg_address = (uint8_t *) 0x05,
220 .page_reg_address = (uint8_t *) 0x81,
221 .single_mask_address = (uint8_t *) 0x0a,
222 .mode_address = (uint8_t *) 0x0b,
223 .flip_flop_address = (uint8_t *) 0x0c,
224 },
225 { /* Channel 3 */
226 .offset_reg_address = (uint8_t *) 0x06,
227 .size_reg_address = (uint8_t *) 0x07,
228 .page_reg_address = (uint8_t *) 0x82,
229 .single_mask_address = (uint8_t *) 0x0a,
230 .mode_address = (uint8_t *) 0x0b,
231 .flip_flop_address = (uint8_t *) 0x0c,
232 },
233
234 /* The second chip 16-bit */
235 { /* Channel 4 - Unusable */
236 .offset_reg_address = (uint8_t *) 0xc0,
237 .size_reg_address = (uint8_t *) 0xc2,
238 .page_reg_address = (uint8_t *) 0x8f,
239 .single_mask_address = (uint8_t *) 0xd4,
240 .mode_address = (uint8_t *) 0xd6,
241 .flip_flop_address = (uint8_t *) 0xd8,
242 },
243 { /* Channel 5 */
244 .offset_reg_address = (uint8_t *) 0xc4,
245 .size_reg_address = (uint8_t *) 0xc6,
246 .page_reg_address = (uint8_t *) 0x8b,
247 .single_mask_address = (uint8_t *) 0xd4,
248 .mode_address = (uint8_t *) 0xd6,
249 .flip_flop_address = (uint8_t *) 0xd8,
250 },
251 { /* Channel 6 */
252 .offset_reg_address = (uint8_t *) 0xc8,
253 .size_reg_address = (uint8_t *) 0xca,
254 .page_reg_address = (uint8_t *) 0x89,
255 .single_mask_address = (uint8_t *) 0xd4,
256 .mode_address = (uint8_t *) 0xd6,
257 .flip_flop_address = (uint8_t *) 0xd8,
258 },
259 { /* Channel 7 */
260 .offset_reg_address = (uint8_t *) 0xcc,
261 .size_reg_address = (uint8_t *) 0xce,
262 .page_reg_address = (uint8_t *) 0x8a,
263 .single_mask_address = (uint8_t *) 0xd4,
264 .mode_address = (uint8_t *) 0xd6,
265 .flip_flop_address = (uint8_t *) 0xd8,
266 },
267 },
268
269 .page_table = NULL,
270 .first = NULL,
271 .second = NULL,
272 .initialized = false,
273};
274
275/* Initialize I/O access to DMA controller I/O ports.
276 *
277 * @param controller DMA Controller structure to initialize.
278 *
279 * @return Error code.
280 *
281 */
282static inline int dma_controller_init(dma_controller_t *controller)
283{
284 assert(controller);
285 int ret = pio_enable(DMA_CONTROLLER_PAGE_BASE, sizeof(dma_page_regs_t),
286 (void **) &controller->page_table);
287 if (ret != EOK)
288 return EIO;
289
290 ret = pio_enable(DMA_CONTROLLER_FIRST_BASE,
291 sizeof(dma_controller_regs_first_t), (void **) &controller->first);
292 if (ret != EOK)
293 return EIO;
294
295 ret = pio_enable(DMA_CONTROLLER_SECOND_BASE,
296 sizeof(dma_controller_regs_second_t), (void **) &controller->second);
297 if (ret != EOK)
298 return EIO;
299
300 controller->initialized = true;
301
302 /* Reset the controller */
303 pio_write_8(&controller->second->master_reset, 0xff);
304 pio_write_8(&controller->first->master_reset, 0xff);
305
306 return EOK;
307}
308
309/** Setup DMA channel to specified place and mode.
310 *
311 * @param channel DMA Channel 1, 2, 3 for 8 bit transfers,
312 * 5, 6, 7 for 16 bit.
313 * @param pa Physical address of the buffer. Must be < 16 MB
314 * for 16 bit and < 1 MB for 8 bit transfers.
315 * @param size DMA buffer size, limited to 64 KB.
316 * @param mode Mode of the DMA channel:
317 * - Read or Write
318 * - Allow automatic reset
319 * - Use address decrement instead of increment
320 * - Use SINGLE/BLOCK/ON DEMAND transfer mode
321 *
322 * @return Error code.
323 */
324int dma_channel_setup(unsigned int channel, uint32_t pa, uint16_t size,
325 uint8_t mode)
326{
327 if ((channel == 0) || (channel == 4))
328 return ENOTSUP;
329
330 if (channel > 7)
331 return ENOENT;
332
333 /* DMA is limited to 24bit addresses. */
334 if (pa >= (1 << 24))
335 return EINVAL;
336
337 /* 8 bit channels use only 4 bits from the page register. */
338 if ((channel > 0) && (channel < 4) && (pa >= (1 << 20)))
339 return EINVAL;
340
341 /* Buffers cannot cross 64K page boundaries */
342 if ((pa & 0xffff0000) != ((pa + size) & 0xffff0000))
343 return EINVAL;
344
345 fibril_mutex_lock(&guard);
346
347 if (!controller_8237.initialized)
348 dma_controller_init(&controller_8237);
349
350 if (!controller_8237.initialized) {
351 fibril_mutex_unlock(&guard);
352 return EIO;
353 }
354
355 /* 16 bit transfers are a bit special */
356 ddf_msg(LVL_DEBUG, "Unspoiled address: %p and size: %zu.", pa, size);
357 if (channel > 4) {
358 /* Size must be aligned to 16 bits */
359 if ((size & 1) != 0) {
360 fibril_mutex_unlock(&guard);
361 return EINVAL;
362 }
363
364 size >>= 1;
365
366 /* Address is fun: lower 16 bits need to be shifted by 1 */
367 pa = ((pa & 0xffff) >> 1) | (pa & 0xff0000);
368 }
369
370 const dma_channel_t dma_channel = controller_8237.channels[channel];
371
372 ddf_msg(LVL_DEBUG, "Setting channel %u, to address %p(%zu), mode %hhx.",
373 channel, pa, size, mode);
374
375 /* Mask DMA request */
376 uint8_t value = DMA_SINGLE_MASK_CHAN_TO_REG(channel) |
377 DMA_SINGLE_MASK_MASKED_FLAG;
378 pio_write_8(dma_channel.single_mask_address, value);
379
380 /* Set mode */
381 value = DMA_MODE_CHAN_TO_REG(channel) | mode;
382 ddf_msg(LVL_DEBUG2, "Writing mode byte: %p:%hhx.",
383 dma_channel.mode_address, value);
384 pio_write_8(dma_channel.mode_address, value);
385
386 /* Set address - reset flip-flop */
387 pio_write_8(dma_channel.flip_flop_address, 0);
388
389 /* Low byte */
390 value = pa & 0xff;
391 ddf_msg(LVL_DEBUG2, "Writing address low byte: %p:%hhx.",
392 dma_channel.offset_reg_address, value);
393 pio_write_8(dma_channel.offset_reg_address, value);
394
395 /* High byte */
396 value = (pa >> 8) & 0xff;
397 ddf_msg(LVL_DEBUG2, "Writing address high byte: %p:%hhx.",
398 dma_channel.offset_reg_address, value);
399 pio_write_8(dma_channel.offset_reg_address, value);
400
401 /* Page address - third byte */
402 value = (pa >> 16) & 0xff;
403 ddf_msg(LVL_DEBUG2, "Writing address page byte: %p:%hhx.",
404 dma_channel.page_reg_address, value);
405 pio_write_8(dma_channel.page_reg_address, value);
406
407 /* Set size - reset flip-flop */
408 pio_write_8(dma_channel.flip_flop_address, 0);
409
410 /* Low byte */
411 value = (size - 1) & 0xff;
412 ddf_msg(LVL_DEBUG2, "Writing size low byte: %p:%hhx.",
413 dma_channel.size_reg_address, value);
414 pio_write_8(dma_channel.size_reg_address, value);
415
416 /* High byte */
417 value = ((size - 1) >> 8) & 0xff;
418 ddf_msg(LVL_DEBUG2, "Writing size high byte: %p:%hhx.",
419 dma_channel.size_reg_address, value);
420 pio_write_8(dma_channel.size_reg_address, value);
421
422 /* Unmask DMA request */
423 value = DMA_SINGLE_MASK_CHAN_TO_REG(channel);
424 pio_write_8(dma_channel.single_mask_address, value);
425
426 fibril_mutex_unlock(&guard);
427
428 return EOK;
429}
430
431extern int dma_channel_remain(unsigned channel, uint16_t *size)
432{
433 assert(size);
434 if ((channel == 0) || (channel == 4))
435 return ENOTSUP;
436
437 if (channel > 7)
438 return ENOENT;
439
440 fibril_mutex_lock(&guard);
441 if (!controller_8237.initialized) {
442 fibril_mutex_unlock(&guard);
443 return EIO;
444 }
445
446 const dma_channel_t dma_channel = controller_8237.channels[channel];
447 /* Get size - reset flip-flop */
448 pio_write_8(dma_channel.flip_flop_address, 0);
449
450 /* Low byte */
451 const uint8_t value_low = pio_read_8(dma_channel.size_reg_address);
452 ddf_msg(LVL_DEBUG2, "Read size low byte: %p:%zx.",
453 dma_channel.size_reg_address, value_low);
454
455 /* High byte */
456 const uint8_t value_high = pio_read_8(dma_channel.size_reg_address);
457 ddf_msg(LVL_DEBUG2, "Read size high byte: %p:%zx.",
458 dma_channel.size_reg_address, value_high);
459 fibril_mutex_unlock(&guard);
460
461 const int remain = (value_high << 8 | value_low) + 1;
462 /* 16 bit DMA size is in words */
463 *size = channel >= 4 ? remain << 1 : remain;
464 return EOK;
465}
466/**
467 * @}
468 */
Note: See TracBrowser for help on using the repository browser.