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

Last change on this file was d1582b50, checked in by Jiri Svoboda <jiri@…>, 5 years ago

Fix spacing in single-line comments using latest ccheck

This found incorrectly formatted section comments (with blocks of
asterisks or dashes). I strongly believe against using section comments
but I am not simply removing them since that would probably be
controversial.

  • Property mode set to 100644
File size: 14.0 KB
RevLine 
[d9cf684a]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>
[3e6a98c5]38#include <stdbool.h>
[d9cf684a]39#include <errno.h>
40#include <ddi.h>
41#include <ddf/log.h>
[3869c596]42#include <fibril_synch.h>
[d9cf684a]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;
[a35b458]101
[d9cf684a]102 uint8_t command_status;
[a35b458]103
[d9cf684a]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;
[a35b458]109
[d9cf684a]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;
[a35b458]138
[d9cf684a]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
[1a37496]190static FIBRIL_MUTEX_INITIALIZE(guard);
[d9cf684a]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 */
[d1582b50]200 { /* Channel 0 - Unusable */
[1cf0a17]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,
[d9cf684a]207 },
[1cf0a17]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,
[d9cf684a]215 },
[1cf0a17]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,
[d9cf684a]223 },
[1cf0a17]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,
[d9cf684a]231 },
[a35b458]232
[d9cf684a]233 /* The second chip 16-bit */
[1cf0a17]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,
[d9cf684a]241 },
[1cf0a17]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,
[d9cf684a]249 },
[1cf0a17]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,
[d9cf684a]257 },
[1cf0a17]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,
[d9cf684a]265 },
266 },
[a35b458]267
[d9cf684a]268 .page_table = NULL,
269 .first = NULL,
270 .second = NULL,
271 .initialized = false,
272};
273
[ed54cbf]274/** Initialize I/O access to DMA controller I/O ports.
[d9cf684a]275 *
276 * @param controller DMA Controller structure to initialize.
277 *
278 * @return Error code.
[561c301]279 *
[d9cf684a]280 */
[b7fd2a0]281static inline errno_t dma_controller_init(dma_controller_t *controller)
[d9cf684a]282{
283 assert(controller);
[b7fd2a0]284 errno_t ret = pio_enable(DMA_CONTROLLER_PAGE_BASE, sizeof(dma_page_regs_t),
[d9cf684a]285 (void **) &controller->page_table);
286 if (ret != EOK)
287 return EIO;
[a35b458]288
[d9cf684a]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;
[a35b458]293
[d9cf684a]294 ret = pio_enable(DMA_CONTROLLER_SECOND_BASE,
[3bacee1]295 sizeof(dma_controller_regs_second_t), (void **) &controller->second);
[d9cf684a]296 if (ret != EOK)
297 return EIO;
[a35b458]298
[d9cf684a]299 controller->initialized = true;
[a35b458]300
[d9cf684a]301 /* Reset the controller */
302 pio_write_8(&controller->second->master_reset, 0xff);
303 pio_write_8(&controller->first->master_reset, 0xff);
[a35b458]304
[d9cf684a]305 return EOK;
306}
307
[ed54cbf]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
[d9cf684a]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 */
[b7fd2a0]341errno_t dma_channel_setup(unsigned int channel, uint32_t pa, uint32_t size,
[d9cf684a]342 uint8_t mode)
343{
[ed54cbf]344 if (!is_dma8(channel) && !is_dma16(channel))
345 return ENOENT;
346
[d9cf684a]347 if ((channel == 0) || (channel == 4))
348 return ENOTSUP;
[a35b458]349
[d9cf684a]350 /* DMA is limited to 24bit addresses. */
351 if (pa >= (1 << 24))
352 return EINVAL;
[a35b458]353
[d9cf684a]354 /* 8 bit channels use only 4 bits from the page register. */
[ed54cbf]355 if (is_dma8(channel) && (pa >= (1 << 20)))
[d9cf684a]356 return EINVAL;
[f6992df]357
358 /* Buffers cannot cross 64K page boundaries */
[c762ad5]359 if ((pa & 0xffff0000) != ((pa + size - 1) & 0xffff0000))
[f6992df]360 return EINVAL;
[a35b458]361
[d9cf684a]362 fibril_mutex_lock(&guard);
[a35b458]363
[d9cf684a]364 if (!controller_8237.initialized)
365 dma_controller_init(&controller_8237);
[a35b458]366
[d9cf684a]367 if (!controller_8237.initialized) {
368 fibril_mutex_unlock(&guard);
369 return EIO;
370 }
[a35b458]371
[ed54cbf]372 /* 16 bit transfers are a bit special */
[05b59393]373 ddf_msg(LVL_DEBUG, "Unspoiled address %#" PRIx32 " (size %" PRIu32 ")",
[f0a2720]374 pa, size);
[ed54cbf]375 if (is_dma16(channel)) {
[d9cf684a]376 /* Size must be aligned to 16 bits */
377 if ((size & 1) != 0) {
378 fibril_mutex_unlock(&guard);
379 return EINVAL;
380 }
[ed54cbf]381 /* Size is in 2byte words */
[d9cf684a]382 size >>= 1;
383 /* Address is fun: lower 16 bits need to be shifted by 1 */
384 pa = ((pa & 0xffff) >> 1) | (pa & 0xff0000);
385 }
[a35b458]386
[d9cf684a]387 const dma_channel_t dma_channel = controller_8237.channels[channel];
[a35b458]388
[f0a2720]389 ddf_msg(LVL_DEBUG, "Setting channel %u to address %#" PRIx32 " "
[05b59393]390 "(size %" PRIu32 "), mode %hhx.", channel, pa, size, mode);
[a35b458]391
[d9cf684a]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);
[a35b458]396
[d9cf684a]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);
[a35b458]402
[d9cf684a]403 /* Set address - reset flip-flop */
404 pio_write_8(dma_channel.flip_flop_address, 0);
[a35b458]405
[d9cf684a]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);
[a35b458]411
[d9cf684a]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);
[a35b458]417
[d9cf684a]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);
[a35b458]423
[d9cf684a]424 /* Set size - reset flip-flop */
425 pio_write_8(dma_channel.flip_flop_address, 0);
[a35b458]426
[d9cf684a]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);
[a35b458]432
[d9cf684a]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);
[a35b458]438
[d9cf684a]439 /* Unmask DMA request */
440 value = DMA_SINGLE_MASK_CHAN_TO_REG(channel);
441 pio_write_8(dma_channel.single_mask_address, value);
[a35b458]442
[d9cf684a]443 fibril_mutex_unlock(&guard);
[a35b458]444
[d9cf684a]445 return EOK;
446}
447
[ed54cbf]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 */
[b7fd2a0]456errno_t dma_channel_remain(unsigned channel, size_t *size)
[1864948]457{
458 assert(size);
[ed54cbf]459 if (!is_dma8(channel) && !is_dma16(channel))
460 return ENOENT;
[a35b458]461
[1864948]462 if ((channel == 0) || (channel == 4))
463 return ENOTSUP;
[a35b458]464
[1864948]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);
[a35b458]474
[1864948]475 /* Low byte */
476 const uint8_t value_low = pio_read_8(dma_channel.size_reg_address);
[e5bc912]477 ddf_msg(LVL_DEBUG2, "Read size low byte: %p:%x.",
[1864948]478 dma_channel.size_reg_address, value_low);
[a35b458]479
[1864948]480 /* High byte */
481 const uint8_t value_high = pio_read_8(dma_channel.size_reg_address);
[e5bc912]482 ddf_msg(LVL_DEBUG2, "Read size high byte: %p:%x.",
[1864948]483 dma_channel.size_reg_address, value_high);
484 fibril_mutex_unlock(&guard);
485
[3bacee1]486 uint16_t remain = (value_high << 8 | value_low);
[7c3fb9b]487 /*
488 * 16 bit DMA size is in words,
[3869c596]489 * the upper bits are bogus for 16bit transfers so we need to get
[7c3fb9b]490 * rid of them. Using limited type works well.
491 */
[3869c596]492 if (is_dma16(channel))
493 remain <<= 1;
[3bacee1]494 *size = is_dma16(channel) ? remain + 2 : remain + 1;
[1864948]495 return EOK;
496}
[d9cf684a]497/**
498 * @}
499 */
Note: See TracBrowser for help on using the repository browser.