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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 01784d2 was 561c301, checked in by Martin Decky <martin@…>, 12 years ago

remove ISA-specific stuff from the comments of generic functions
do not check whether the return value of async_exchange_begin() is NULL (the functions working with the exchange can handle it gracefully)
coding style

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