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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3be9d10 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

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