source: mainline/uspace/drv/audio/sb16/dma_controller.c@ b130d0e

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

sb16: Dump PNP registers on startup. Move well-known mixer addresses to one place.

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