HelenOS sources
This source file includes following definitions.
- dma_controller_init
- is_dma16
- is_dma8
- dma_channel_setup
- dma_channel_remain
#include <assert.h>
#include <stdbool.h>
#include <errno.h>
#include <ddi.h>
#include <ddf/log.h>
#include <fibril_synch.h>
#include "i8237.h"
#define DMA_CONTROLLER_FIRST_BASE ((void *) 0x00)
#define DMA_CONTROLLER_SECOND_BASE ((void *) 0xc0)
#define DMA_CONTROLLER_PAGE_BASE ((void *) 0x81)
#define DMA_STATUS_REQ(x) (1 << (((x) % 4) + 4))
#define DMA_STATUS_COMPLETE(x) (1 << ((x) % 4))
#define DMA_COMMAND_COND (1 << 2)
#define DMA_SINGLE_MASK_CHAN_SEL_MASK 0x03
#define DMA_SINGLE_MASK_CHAN_SEL_SHIFT 0
#define DMA_SINGLE_MASK_CHAN_TO_REG(x) \
(((x) & DMA_SINGLE_MASK_CHAN_SEL_MASK) << DMA_SINGLE_MASK_CHAN_SEL_SHIFT)
#define DMA_SINGLE_MASK_MASKED_FLAG (1 << 2)
#define DMA_MODE_CHAN_SELECT_MASK 0x03
#define DMA_MODE_CHAN_SELECT_SHIFT 0
#define DMA_MODE_CHAN_TO_REG(x) \
(((x) & DMA_MODE_CHAN_SELECT_MASK) << DMA_MODE_CHAN_SELECT_SHIFT)
#define DMA_MODE_CHAN_TRA_MASK 0x03
#define DMA_MODE_CHAN_TRA_SHIFT 2
#define DMA_MODE_CHAN_TRA_SELF_TEST 0
#define DMA_MODE_CHAN_TRA_WRITE 0x01
#define DMA_MODE_CHAN_TRA_READ 0x02
#define DMA_MODE_CHAN_AUTO_FLAG (1 << 4)
#define DMA_MODE_CHAN_DOWN_FLAG (1 << 5)
#define DMA_MODE_CHAN_MODE_MASK 0x03
#define DMA_MODE_CHAN_MODE_SHIFT 6
#define DMA_MODE_CHAN_MODE_DEMAND 0
#define DMA_MODE_CHAN_MODE_SINGLE 1
#define DMA_MODE_CHAN_MODE_BLOCK 2
#define DMA_MODE_CHAN_MODE_CASCADE 3
#define DMA_MULTI_MASK_CHAN(x) (1 << ((x) % 4))
typedef struct {
uint8_t channel_start0;
uint8_t channel_count0;
uint8_t channel_start1;
uint8_t channel_count1;
uint8_t channel_start2;
uint8_t channel_count2;
uint8_t channel_start3;
uint8_t channel_count3;
uint8_t command_status;
uint8_t request;
uint8_t single_mask;
uint8_t mode;
uint8_t flip_flop;
uint8_t master_reset;
uint8_t mask_reset;
uint8_t multi_mask;
} dma_controller_regs_first_t;
typedef struct {
uint8_t channel_start4;
uint8_t reserved0;
uint8_t channel_count4;
uint8_t reserved1;
uint8_t channel_start5;
uint8_t reserved2;
uint8_t channel_count5;
uint8_t reserved3;
uint8_t channel_start6;
uint8_t reserved4;
uint8_t channel_count6;
uint8_t reserved5;
uint8_t channel_start7;
uint8_t reserved6;
uint8_t channel_count7;
uint8_t command_status;
uint8_t reserved8;
uint8_t request;
uint8_t reserved9;
uint8_t single_mask;
uint8_t reserveda;
uint8_t mode;
uint8_t reservedb;
uint8_t flip_flop;
uint8_t reservedc;
uint8_t master_reset;
uint8_t reservedd;
uint8_t multi_mask;
} dma_controller_regs_second_t;
typedef struct {
uint8_t channel2;
uint8_t channel3;
uint8_t channel1;
uint8_t reserved0;
uint8_t reserved1;
uint8_t reserved2;
uint8_t channel0;
uint8_t reserved3;
uint8_t channel6;
uint8_t channel7;
uint8_t channel5;
uint8_t reserved4;
uint8_t reserved5;
uint8_t reserved6;
uint8_t channel4;
} dma_page_regs_t;
typedef struct {
ioport8_t *offset_reg_address;
ioport8_t *size_reg_address;
ioport8_t *page_reg_address;
ioport8_t *single_mask_address;
ioport8_t *mode_address;
ioport8_t *flip_flop_address;
} dma_channel_t;
typedef struct {
dma_channel_t channels[8];
dma_page_regs_t *page_table;
dma_controller_regs_first_t *first;
dma_controller_regs_second_t *second;
bool initialized;
} dma_controller_t;
static FIBRIL_MUTEX_INITIALIZE(guard);
static dma_controller_t controller_8237 = {
.channels = {
{
.offset_reg_address = (uint8_t *) 0x00,
.size_reg_address = (uint8_t *) 0x01,
.page_reg_address = (uint8_t *) 0x87,
.single_mask_address = (uint8_t *) 0x0a,
.mode_address = (uint8_t *) 0x0b,
.flip_flop_address = (uint8_t *) 0x0c,
},
{
.offset_reg_address = (uint8_t *) 0x02,
.size_reg_address = (uint8_t *) 0x03,
.page_reg_address = (uint8_t *) 0x83,
.single_mask_address = (uint8_t *) 0x0a,
.mode_address = (uint8_t *) 0x0b,
.flip_flop_address = (uint8_t *) 0x0c,
},
{
.offset_reg_address = (uint8_t *) 0x04,
.size_reg_address = (uint8_t *) 0x05,
.page_reg_address = (uint8_t *) 0x81,
.single_mask_address = (uint8_t *) 0x0a,
.mode_address = (uint8_t *) 0x0b,
.flip_flop_address = (uint8_t *) 0x0c,
},
{
.offset_reg_address = (uint8_t *) 0x06,
.size_reg_address = (uint8_t *) 0x07,
.page_reg_address = (uint8_t *) 0x82,
.single_mask_address = (uint8_t *) 0x0a,
.mode_address = (uint8_t *) 0x0b,
.flip_flop_address = (uint8_t *) 0x0c,
},
{
.offset_reg_address = (uint8_t *) 0xc0,
.size_reg_address = (uint8_t *) 0xc2,
.page_reg_address = (uint8_t *) 0x8f,
.single_mask_address = (uint8_t *) 0xd4,
.mode_address = (uint8_t *) 0xd6,
.flip_flop_address = (uint8_t *) 0xd8,
},
{
.offset_reg_address = (uint8_t *) 0xc4,
.size_reg_address = (uint8_t *) 0xc6,
.page_reg_address = (uint8_t *) 0x8b,
.single_mask_address = (uint8_t *) 0xd4,
.mode_address = (uint8_t *) 0xd6,
.flip_flop_address = (uint8_t *) 0xd8,
},
{
.offset_reg_address = (uint8_t *) 0xc8,
.size_reg_address = (uint8_t *) 0xca,
.page_reg_address = (uint8_t *) 0x89,
.single_mask_address = (uint8_t *) 0xd4,
.mode_address = (uint8_t *) 0xd6,
.flip_flop_address = (uint8_t *) 0xd8,
},
{
.offset_reg_address = (uint8_t *) 0xcc,
.size_reg_address = (uint8_t *) 0xce,
.page_reg_address = (uint8_t *) 0x8a,
.single_mask_address = (uint8_t *) 0xd4,
.mode_address = (uint8_t *) 0xd6,
.flip_flop_address = (uint8_t *) 0xd8,
},
},
.page_table = NULL,
.first = NULL,
.second = NULL,
.initialized = false,
};
static inline errno_t dma_controller_init(dma_controller_t *controller)
{
assert(controller);
errno_t ret = pio_enable(DMA_CONTROLLER_PAGE_BASE, sizeof(dma_page_regs_t),
(void **) &controller->page_table);
if (ret != EOK)
return EIO;
ret = pio_enable(DMA_CONTROLLER_FIRST_BASE,
sizeof(dma_controller_regs_first_t), (void **) &controller->first);
if (ret != EOK)
return EIO;
ret = pio_enable(DMA_CONTROLLER_SECOND_BASE,
sizeof(dma_controller_regs_second_t), (void **) &controller->second);
if (ret != EOK)
return EIO;
controller->initialized = true;
pio_write_8(&controller->second->master_reset, 0xff);
pio_write_8(&controller->first->master_reset, 0xff);
return EOK;
}
static inline bool is_dma16(unsigned channel)
{
return (channel >= 4) && (channel < 8);
}
static inline bool is_dma8(unsigned channel)
{
return (channel < 4);
}
errno_t dma_channel_setup(unsigned int channel, uint32_t pa, uint32_t size,
uint8_t mode)
{
if (!is_dma8(channel) && !is_dma16(channel))
return ENOENT;
if ((channel == 0) || (channel == 4))
return ENOTSUP;
if (pa >= (1 << 24))
return EINVAL;
if (is_dma8(channel) && (pa >= (1 << 20)))
return EINVAL;
if ((pa & 0xffff0000) != ((pa + size - 1) & 0xffff0000))
return EINVAL;
fibril_mutex_lock(&guard);
if (!controller_8237.initialized)
dma_controller_init(&controller_8237);
if (!controller_8237.initialized) {
fibril_mutex_unlock(&guard);
return EIO;
}
ddf_msg(LVL_DEBUG, "Unspoiled address %#" PRIx32 " (size %" PRIu32 ")",
pa, size);
if (is_dma16(channel)) {
if ((size & 1) != 0) {
fibril_mutex_unlock(&guard);
return EINVAL;
}
size >>= 1;
pa = ((pa & 0xffff) >> 1) | (pa & 0xff0000);
}
const dma_channel_t dma_channel = controller_8237.channels[channel];
ddf_msg(LVL_DEBUG, "Setting channel %u to address %#" PRIx32 " "
"(size %" PRIu32 "), mode %hhx.", channel, pa, size, mode);
uint8_t value = DMA_SINGLE_MASK_CHAN_TO_REG(channel) |
DMA_SINGLE_MASK_MASKED_FLAG;
pio_write_8(dma_channel.single_mask_address, value);
value = DMA_MODE_CHAN_TO_REG(channel) | mode;
ddf_msg(LVL_DEBUG2, "Writing mode byte: %p:%hhx.",
dma_channel.mode_address, value);
pio_write_8(dma_channel.mode_address, value);
pio_write_8(dma_channel.flip_flop_address, 0);
value = pa & 0xff;
ddf_msg(LVL_DEBUG2, "Writing address low byte: %p:%hhx.",
dma_channel.offset_reg_address, value);
pio_write_8(dma_channel.offset_reg_address, value);
value = (pa >> 8) & 0xff;
ddf_msg(LVL_DEBUG2, "Writing address high byte: %p:%hhx.",
dma_channel.offset_reg_address, value);
pio_write_8(dma_channel.offset_reg_address, value);
value = (pa >> 16) & 0xff;
ddf_msg(LVL_DEBUG2, "Writing address page byte: %p:%hhx.",
dma_channel.page_reg_address, value);
pio_write_8(dma_channel.page_reg_address, value);
pio_write_8(dma_channel.flip_flop_address, 0);
value = (size - 1) & 0xff;
ddf_msg(LVL_DEBUG2, "Writing size low byte: %p:%hhx.",
dma_channel.size_reg_address, value);
pio_write_8(dma_channel.size_reg_address, value);
value = ((size - 1) >> 8) & 0xff;
ddf_msg(LVL_DEBUG2, "Writing size high byte: %p:%hhx.",
dma_channel.size_reg_address, value);
pio_write_8(dma_channel.size_reg_address, value);
value = DMA_SINGLE_MASK_CHAN_TO_REG(channel);
pio_write_8(dma_channel.single_mask_address, value);
fibril_mutex_unlock(&guard);
return EOK;
}
errno_t dma_channel_remain(unsigned channel, size_t *size)
{
assert(size);
if (!is_dma8(channel) && !is_dma16(channel))
return ENOENT;
if ((channel == 0) || (channel == 4))
return ENOTSUP;
fibril_mutex_lock(&guard);
if (!controller_8237.initialized) {
fibril_mutex_unlock(&guard);
return EIO;
}
const dma_channel_t dma_channel = controller_8237.channels[channel];
pio_write_8(dma_channel.flip_flop_address, 0);
const uint8_t value_low = pio_read_8(dma_channel.size_reg_address);
ddf_msg(LVL_DEBUG2, "Read size low byte: %p:%x.",
dma_channel.size_reg_address, value_low);
const uint8_t value_high = pio_read_8(dma_channel.size_reg_address);
ddf_msg(LVL_DEBUG2, "Read size high byte: %p:%x.",
dma_channel.size_reg_address, value_high);
fibril_mutex_unlock(&guard);
uint16_t remain = (value_high << 8 | value_low);
if (is_dma16(channel))
remain <<= 1;
*size = is_dma16(channel) ? remain + 2 : remain + 1;
return EOK;
}
HelenOS homepage, sources at GitHub