source: mainline/uspace/drv/bus/isa/dma_controller.c@ 1b93658

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

isa: Add mutex to guard access to DMA controller.

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