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

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

Fix printf compile issues

  • 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 */
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/** Helper function. Channels 4,5,6, and 7 are 8 bit DMA.
310 * @pram channel DMA channel.
311 * @reutrn True, if channel is 4,5,6, or 7, false otherwise.
312 */
313static inline bool is_dma16(unsigned channel)
314{
315 return (channel >= 4) && (channel < 8);
316}
317
318/** Helper function. Channels 0,1,2, and 3 are 8 bit DMA.
319 * @pram channel DMA channel.
320 * @reutrn True, if channel is 0,1,2, or 3, false otherwise.
321 */
322static inline bool is_dma8(unsigned channel)
323{
324 return (channel < 4);
325}
326
327/** Setup DMA channel to specified place and mode.
328 *
329 * @param channel DMA Channel 1, 2, 3 for 8 bit transfers,
330 * 5, 6, 7 for 16 bit.
331 * @param pa Physical address of the buffer. Must be < 16 MB
332 * for 16 bit and < 1 MB for 8 bit transfers.
333 * @param size DMA buffer size, limited to 64 KB.
334 * @param mode Mode of the DMA channel:
335 * - Read or Write
336 * - Allow automatic reset
337 * - Use address decrement instead of increment
338 * - Use SINGLE/BLOCK/ON DEMAND transfer mode
339 *
340 * @return Error code.
341 */
342int dma_channel_setup(unsigned int channel, uint32_t pa, uint32_t size,
343 uint8_t mode)
344{
345 if (!is_dma8(channel) && !is_dma16(channel))
346 return ENOENT;
347
348 if ((channel == 0) || (channel == 4))
349 return ENOTSUP;
350
351 /* DMA is limited to 24bit addresses. */
352 if (pa >= (1 << 24))
353 return EINVAL;
354
355 /* 8 bit channels use only 4 bits from the page register. */
356 if (is_dma8(channel) && (pa >= (1 << 20)))
357 return EINVAL;
358
359 /* Buffers cannot cross 64K page boundaries */
360 if ((pa & 0xffff0000) != ((pa + size - 1) & 0xffff0000))
361 return EINVAL;
362
363 fibril_mutex_lock(&guard);
364
365 if (!controller_8237.initialized)
366 dma_controller_init(&controller_8237);
367
368 if (!controller_8237.initialized) {
369 fibril_mutex_unlock(&guard);
370 return EIO;
371 }
372
373 /* 16 bit transfers are a bit special */
374 ddf_msg(LVL_DEBUG, "Unspoiled address %#" PRIx32 " (size %" PRIu16 ")",
375 pa, size);
376 if (is_dma16(channel)) {
377 /* Size must be aligned to 16 bits */
378 if ((size & 1) != 0) {
379 fibril_mutex_unlock(&guard);
380 return EINVAL;
381 }
382 /* Size is in 2byte words */
383 size >>= 1;
384 /* Address is fun: lower 16 bits need to be shifted by 1 */
385 pa = ((pa & 0xffff) >> 1) | (pa & 0xff0000);
386 }
387
388 const dma_channel_t dma_channel = controller_8237.channels[channel];
389
390 ddf_msg(LVL_DEBUG, "Setting channel %u to address %#" PRIx32 " "
391 "(size %" PRIu16 "), mode %hhx.", channel, pa, size, mode);
392
393 /* Mask DMA request */
394 uint8_t value = DMA_SINGLE_MASK_CHAN_TO_REG(channel) |
395 DMA_SINGLE_MASK_MASKED_FLAG;
396 pio_write_8(dma_channel.single_mask_address, value);
397
398 /* Set mode */
399 value = DMA_MODE_CHAN_TO_REG(channel) | mode;
400 ddf_msg(LVL_DEBUG2, "Writing mode byte: %p:%hhx.",
401 dma_channel.mode_address, value);
402 pio_write_8(dma_channel.mode_address, value);
403
404 /* Set address - reset flip-flop */
405 pio_write_8(dma_channel.flip_flop_address, 0);
406
407 /* Low byte */
408 value = pa & 0xff;
409 ddf_msg(LVL_DEBUG2, "Writing address low byte: %p:%hhx.",
410 dma_channel.offset_reg_address, value);
411 pio_write_8(dma_channel.offset_reg_address, value);
412
413 /* High byte */
414 value = (pa >> 8) & 0xff;
415 ddf_msg(LVL_DEBUG2, "Writing address high byte: %p:%hhx.",
416 dma_channel.offset_reg_address, value);
417 pio_write_8(dma_channel.offset_reg_address, value);
418
419 /* Page address - third byte */
420 value = (pa >> 16) & 0xff;
421 ddf_msg(LVL_DEBUG2, "Writing address page byte: %p:%hhx.",
422 dma_channel.page_reg_address, value);
423 pio_write_8(dma_channel.page_reg_address, value);
424
425 /* Set size - reset flip-flop */
426 pio_write_8(dma_channel.flip_flop_address, 0);
427
428 /* Low byte */
429 value = (size - 1) & 0xff;
430 ddf_msg(LVL_DEBUG2, "Writing size low byte: %p:%hhx.",
431 dma_channel.size_reg_address, value);
432 pio_write_8(dma_channel.size_reg_address, value);
433
434 /* High byte */
435 value = ((size - 1) >> 8) & 0xff;
436 ddf_msg(LVL_DEBUG2, "Writing size high byte: %p:%hhx.",
437 dma_channel.size_reg_address, value);
438 pio_write_8(dma_channel.size_reg_address, value);
439
440 /* Unmask DMA request */
441 value = DMA_SINGLE_MASK_CHAN_TO_REG(channel);
442 pio_write_8(dma_channel.single_mask_address, value);
443
444 fibril_mutex_unlock(&guard);
445
446 return EOK;
447}
448
449/** Query remaining buffer size.
450 *
451 * @param channel DMA Channel 1, 2, 3 for 8 bit transfers,
452 * 5, 6, 7 for 16 bit.
453 * @param size Place to store number of bytes pending in the assigned buffer.
454 *
455 * @return Error code.
456 */
457int dma_channel_remain(unsigned channel, size_t *size)
458{
459 assert(size);
460 if (!is_dma8(channel) && !is_dma16(channel))
461 return ENOENT;
462
463 if ((channel == 0) || (channel == 4))
464 return ENOTSUP;
465
466 fibril_mutex_lock(&guard);
467 if (!controller_8237.initialized) {
468 fibril_mutex_unlock(&guard);
469 return EIO;
470 }
471
472 const dma_channel_t dma_channel = controller_8237.channels[channel];
473 /* Get size - reset flip-flop */
474 pio_write_8(dma_channel.flip_flop_address, 0);
475
476 /* Low byte */
477 const uint8_t value_low = pio_read_8(dma_channel.size_reg_address);
478 ddf_msg(LVL_DEBUG2, "Read size low byte: %p:%x.",
479 dma_channel.size_reg_address, value_low);
480
481 /* High byte */
482 const uint8_t value_high = pio_read_8(dma_channel.size_reg_address);
483 ddf_msg(LVL_DEBUG2, "Read size high byte: %p:%x.",
484 dma_channel.size_reg_address, value_high);
485 fibril_mutex_unlock(&guard);
486
487 uint16_t remain = (value_high << 8 | value_low) ;
488 /* 16 bit DMA size is in words,
489 * the upper bits are bogus for 16bit transfers so we need to get
490 * rid of them. Using limited type works well.*/
491 if (is_dma16(channel))
492 remain <<= 1;
493 *size = is_dma16(channel) ? remain + 2: remain + 1;
494 return EOK;
495}
496/**
497 * @}
498 */
Note: See TracBrowser for help on using the repository browser.