Files
retrobsd/tools/virtualmips/adm5120/adm5120_dev_uart.c
2014-04-09 14:27:18 +01:00

258 lines
7.5 KiB
C

/*
* Copyright (C) yajin 2008 <yajinzhou@gmail.com >
*
* This file is part of the virtualmips distribution.
* See LICENSE file for terms of the license.
*
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include "device.h"
#include "mips_memory.h"
#include "adm5120.h"
#include "cpu.h"
m_uint32_t uart_table[2][UART_INDEX_MAX];
struct uart_data {
struct vdevice *dev;
m_uint8_t *uart_ptr;
m_uint32_t uart_size;
vtty_t *vtty[2];
};
#define ADM5120_UART_IRQ0 1
#define ADM5120_UART_IRQ1 2
void uart_set_interrupt (cpu_mips_t * cpu, int channel)
{
if (channel == 0)
cpu->vm->set_irq (cpu->vm, INT_LVL_UART0);
else if (channel == 1)
cpu->vm->set_irq (cpu->vm, INT_LVL_UART1);
return;
assert (0);
}
void uart_clear_interrupt (cpu_mips_t * cpu, int channel)
{
if (channel == 0)
cpu->vm->clear_irq (cpu->vm, INT_LVL_UART0);
else if (channel == 1)
cpu->vm->clear_irq (cpu->vm, INT_LVL_UART1);
return;
assert (0);
}
/* Console port input */
static void tty_con0_input (vtty_t * vtty)
{
uart_table[0][UART_FR_REG / 4] &= ~UART_RX_FIFO_EMPTY;
if (vtty_is_full (vtty))
uart_table[0][UART_FR_REG / 4] |= UART_RX_FIFO_FULL;
if ((uart_table[0][UART_CR_REG / 4] & UART_RX_INT_EN)
&& (uart_table[0][UART_CR_REG / 4] & UART_PORT_EN)) {
uart_table[0][UART_ICR_REG / 4] |= UART_RX_INT;
uart_set_interrupt (vtty->vm->boot_cpu, 0);
}
}
/* Console port input */
static void tty_con1_input (vtty_t * vtty)
{
uart_table[1][UART_FR_REG / 4] &= ~UART_RX_FIFO_EMPTY;
if (vtty_is_full (vtty))
uart_table[1][UART_FR_REG / 4] |= UART_RX_FIFO_FULL;
if ((uart_table[1][UART_CR_REG / 4] & UART_RX_INT_EN)
&& (uart_table[1][UART_CR_REG / 4] & UART_PORT_EN)) {
uart_set_interrupt (vtty->vm->boot_cpu, 1);
uart_table[1][UART_ICR_REG / 4] |= UART_RX_INT;
}
}
void *dev_uart_access (cpu_mips_t * cpu, struct vdevice *dev,
m_uint32_t offset, u_int op_size, u_int op_type,
m_reg_t * data, m_uint8_t * has_set_value, m_uint8_t channel)
{
struct uart_data *d = dev->priv_data;
if (offset >= d->uart_size) {
*data = 0;
return NULL;
}
switch (offset) {
case (UART_DR_REG):
if (!(uart_table[channel][UART_CR_REG / 4] & UART_PORT_EN)) {
//uart port is disabled.
if (op_type == MTS_READ) {
*data = vmtoh32 (0xffffffff);
}
*has_set_value = TRUE;
return NULL;
}
if (op_type == MTS_READ) {
if (vtty_is_char_avail (d->vtty[channel])) {
*data = vtty_get_char (d->vtty[channel]);
uart_table[channel][UART_RSR_REG / 4] = 0;
} else
*data = vmtoh32 (0xffffffff);
if (vtty_is_char_avail (d->vtty[channel])) {
uart_table[channel][UART_FR_REG / 4] &= ~UART_RX_FIFO_EMPTY;
uart_table[channel][UART_FR_REG / 4] |= UART_RX_FIFO_FULL;
if ((uart_table[channel][UART_CR_REG / 4] & UART_RX_INT_EN)
&& (uart_table[channel][UART_CR_REG / 4] & UART_PORT_EN)) {
uart_table[channel][UART_ICR_REG / 4] |= UART_RX_INT;
uart_set_interrupt (cpu, channel);
}
} else {
uart_table[channel][UART_FR_REG / 4] |= UART_RX_FIFO_EMPTY;
uart_table[channel][UART_FR_REG / 4] &= ~UART_RX_FIFO_FULL;
if ((uart_table[channel][UART_CR_REG / 4] & UART_RX_INT_EN)
&& (uart_table[channel][UART_CR_REG / 4] & UART_PORT_EN)) {
uart_table[channel][UART_ICR_REG / 4] &= ~UART_RX_INT;
uart_clear_interrupt (cpu, channel);
}
}
*has_set_value = TRUE;
} else if (op_type == MTS_WRITE) {
vtty_put_char (d->vtty[channel], (char) *data);
*has_set_value = TRUE;
} else {
assert (0);
}
return NULL;
break;
case UART_RSR_REG:
if (op_type == MTS_WRITE) {
uart_table[channel][UART_RSR_REG / 4] = 0;
*has_set_value = TRUE;
}
break;
case UART_CR_REG:
if (op_type == MTS_WRITE) {
//enable UART
if ((*data) & UART_PORT_EN) {
if (*data & UART_TX_INT_EN) {
//START TX
uart_table[channel][UART_ICR_REG / 4] |= UART_TX_INT;
uart_set_interrupt (cpu, channel);
} else {
//TX interrupt dissabled
uart_table[channel][UART_ICR_REG / 4] &= ~UART_TX_INT;
uart_clear_interrupt (cpu, channel);
}
if (*data & UART_RX_INT_EN) {
if (vtty_is_char_avail (d->vtty[channel])) {
//set RX interrupt
uart_table[channel][UART_ICR_REG / 4] |= UART_RX_INT;
uart_set_interrupt (cpu, channel);
}
} else {
//disable RX interrupt
uart_table[channel][UART_ICR_REG / 4] &= ~UART_RX_INT;
uart_clear_interrupt (cpu, channel);
}
} else {
//disable UART
//clear rx and tx interrupt
uart_table[channel][UART_ICR_REG / 4] &= ~UART_TX_INT;
uart_clear_interrupt (cpu, channel);
uart_table[channel][UART_ICR_REG / 4] &= ~UART_RX_INT;
uart_clear_interrupt (cpu, channel);
}
}
break;
}
return ((void *) (d->uart_ptr + offset));
}
void dev_uart_init_defaultvalue (int uart_index)
{
uart_table[uart_index][UART_FR_REG / 4] = 0x90;
uart_table[uart_index][UART_RSR_REG / 4] = 0;
}
void *dev_uart_access0 (cpu_mips_t * cpu, struct vdevice *dev,
m_uint32_t offset, u_int op_size, u_int op_type, m_reg_t * data,
m_uint8_t * has_set_value)
{
return dev_uart_access (cpu, dev, offset, op_size, op_type, data,
has_set_value, 0);
}
void *dev_uart_access1 (cpu_mips_t * cpu, struct vdevice *dev,
m_uint32_t offset, u_int op_size, u_int op_type, m_reg_t * data,
m_uint8_t * has_set_value)
{
return dev_uart_access (cpu, dev, offset, op_size, op_type, data,
has_set_value, 1);
}
int dev_uart_init (vm_instance_t * vm, char *name, m_pa_t paddr,
m_uint32_t len, vtty_t * vtty, int uart_index)
{
struct uart_data *d;
/* allocate the private data structure */
if (!(d = malloc (sizeof (*d)))) {
fprintf (stderr, "UART: unable to create device.\n");
return (-1);
}
memset (d, 0, sizeof (*d));
if (!(d->dev = dev_create (name)))
goto err_dev_create;
d->dev->priv_data = d;
d->dev->phys_addr = paddr;
d->dev->phys_len = len;
d->dev->flags = VDEVICE_FLAG_NO_MTS_MMAP;
(*d).vtty[uart_index] = vtty;
d->uart_size = len;
if (uart_index == 0) {
d->dev->handler = dev_uart_access0;
(*d).vtty[uart_index]->read_notifier = tty_con0_input;
} else {
d->dev->handler = dev_uart_access1;
(*d).vtty[uart_index]->read_notifier = tty_con1_input;
}
d->uart_ptr = (m_uint8_t *) (m_iptr_t) (&uart_table[uart_index]);
vm_bind_device (vm, d->dev);
dev_uart_init_defaultvalue (uart_index);
return (0);
err_dev_create:
free (d);
return (-1);
}