x49gp/s3c2410_uart.c
2017-10-26 14:30:39 +02:00

481 lines
11 KiB
C

/* $Id: s3c2410_uart.c,v 1.4 2008/12/11 12:18:17 ecd Exp $
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <x49gp.h>
#include <s3c2410.h>
#include <s3c2410_intc.h>
typedef struct {
uint32_t ulcon;
uint32_t ucon;
uint32_t ufcon;
uint32_t umcon;
uint32_t utrstat;
uint32_t uerstat;
uint32_t ufstat;
uint32_t umstat;
uint32_t utxh;
uint32_t urxh;
uint32_t ubrdiv;
int int_err;
int int_txd;
int int_rxd;
unsigned int nr_regs;
s3c2410_offset_t *regs;
x49gp_t *x49gp;
} s3c2410_uart_reg_t;
typedef struct {
s3c2410_uart_reg_t uart[3];
} s3c2410_uart_t;
static int
s3c2410_uart_data_init(s3c2410_uart_t *uart)
{
s3c2410_offset_t regs0[] = {
S3C2410_OFFSET(UART0, ULCON, 0x00000000, uart->uart[0].ulcon),
S3C2410_OFFSET(UART0, UCON, 0x00000000, uart->uart[0].ucon),
S3C2410_OFFSET(UART0, UFCON, 0x00000000, uart->uart[0].ufcon),
S3C2410_OFFSET(UART0, UMCON, 0x00000000, uart->uart[0].umcon),
S3C2410_OFFSET(UART0, UTRSTAT, 0x00000006, uart->uart[0].utrstat),
S3C2410_OFFSET(UART0, UERSTAT, 0x00000000, uart->uart[0].uerstat),
S3C2410_OFFSET(UART0, UFSTAT, 0x00000000, uart->uart[0].ufstat),
S3C2410_OFFSET(UART0, UMSTAT, 0x00000000, uart->uart[0].umstat),
S3C2410_OFFSET(UART0, UTXH, 0, uart->uart[0].utxh),
S3C2410_OFFSET(UART0, URXH, 0, uart->uart[0].urxh),
S3C2410_OFFSET(UART0, UBRDIV, 0, uart->uart[0].ubrdiv)
};
s3c2410_offset_t regs1[] = {
S3C2410_OFFSET(UART1, ULCON, 0x00000000, uart->uart[1].ulcon),
S3C2410_OFFSET(UART1, UCON, 0x00000000, uart->uart[1].ucon),
S3C2410_OFFSET(UART1, UFCON, 0x00000000, uart->uart[1].ufcon),
S3C2410_OFFSET(UART1, UMCON, 0x00000000, uart->uart[1].umcon),
S3C2410_OFFSET(UART1, UTRSTAT, 0x00000006, uart->uart[1].utrstat),
S3C2410_OFFSET(UART1, UERSTAT, 0x00000000, uart->uart[1].uerstat),
S3C2410_OFFSET(UART1, UFSTAT, 0x00000000, uart->uart[1].ufstat),
S3C2410_OFFSET(UART1, UMSTAT, 0x00000000, uart->uart[1].umstat),
S3C2410_OFFSET(UART1, UTXH, 0, uart->uart[1].utxh),
S3C2410_OFFSET(UART1, URXH, 0, uart->uart[1].urxh),
S3C2410_OFFSET(UART1, UBRDIV, 0, uart->uart[1].ubrdiv)
};
s3c2410_offset_t regs2[] = {
S3C2410_OFFSET(UART2, ULCON, 0x00000000, uart->uart[2].ulcon),
S3C2410_OFFSET(UART2, UCON, 0x00000000, uart->uart[2].ucon),
S3C2410_OFFSET(UART2, UFCON, 0x00000000, uart->uart[2].ufcon),
S3C2410_OFFSET(UART2, UTRSTAT, 0x00000006, uart->uart[2].utrstat),
S3C2410_OFFSET(UART2, UERSTAT, 0x00000000, uart->uart[2].uerstat),
S3C2410_OFFSET(UART2, UFSTAT, 0x00000000, uart->uart[2].ufstat),
S3C2410_OFFSET(UART2, UTXH, 0, uart->uart[2].utxh),
S3C2410_OFFSET(UART2, URXH, 0, uart->uart[2].urxh),
S3C2410_OFFSET(UART2, UBRDIV, 0, uart->uart[2].ubrdiv)
};
uart->uart[0].regs = malloc(sizeof(regs0));
if (NULL == uart->uart[0].regs) {
fprintf(stderr, "%s:%u: Out of memory\n",
__FUNCTION__, __LINE__);
return -ENOMEM;
}
uart->uart[1].regs = malloc(sizeof(regs1));
if (NULL == uart->uart[1].regs) {
fprintf(stderr, "%s:%u: Out of memory\n",
__FUNCTION__, __LINE__);
free(uart->uart[0].regs);
return -ENOMEM;
}
uart->uart[2].regs = malloc(sizeof(regs2));
if (NULL == uart->uart[2].regs) {
fprintf(stderr, "%s:%u: Out of memory\n",
__FUNCTION__, __LINE__);
free(uart->uart[0].regs);
free(uart->uart[1].regs);
return -ENOMEM;
}
memcpy(uart->uart[0].regs, regs0, sizeof(regs0));
uart->uart[0].nr_regs = sizeof(regs0) / sizeof(regs0[0]);
uart->uart[0].int_err = SUB_INT_ERR0;
uart->uart[0].int_txd = SUB_INT_TXD0;
uart->uart[0].int_rxd = SUB_INT_RXD0;
memcpy(uart->uart[1].regs, regs1, sizeof(regs1));
uart->uart[1].nr_regs = sizeof(regs1) / sizeof(regs1[0]);
uart->uart[1].int_err = SUB_INT_ERR1;
uart->uart[1].int_txd = SUB_INT_TXD1;
uart->uart[1].int_rxd = SUB_INT_RXD1;
memcpy(uart->uart[2].regs, regs2, sizeof(regs2));
uart->uart[2].nr_regs = sizeof(regs2) / sizeof(regs2[0]);
uart->uart[2].int_err = SUB_INT_ERR2;
uart->uart[2].int_txd = SUB_INT_TXD2;
uart->uart[2].int_rxd = SUB_INT_RXD2;
return 0;
}
static uint32_t
s3c2410_uart_read(void *opaque, target_phys_addr_t offset)
{
s3c2410_uart_reg_t *uart_regs = opaque;
x49gp_t *x49gp = uart_regs->x49gp;
s3c2410_offset_t *reg;
#ifdef DEBUG_S3C2410_UART
const char *module;
uint32_t mod_offset;
uint32_t base;
base = (offset & 0x0000c000) >> 14;
switch (base) {
case 0:
module = "s3c2410-uart0";
mod_offset = S3C2410_UART0_BASE;
break;
case 1:
module = "s3c2410-uart1";
mod_offset = S3C2410_UART1_BASE;
break;
case 2:
module = "s3c2410-uart2";
mod_offset = S3C2410_UART2_BASE;
break;
default:
return ~(0);
}
#endif
offset &= ~(0xffffc000);
if (! S3C2410_OFFSET_OK(uart_regs, offset)) {
return ~(0);
}
reg = S3C2410_OFFSET_ENTRY(uart_regs, offset);
#ifdef DEBUG_S3C2410_UART
printf("read %s [%08x] %s [%08lx] data %08x\n",
module, mod_offset, reg->name, (unsigned long) offset, *(reg->datap));
#endif
switch (offset) {
case S3C2410_UART0_URXH:
uart_regs->utrstat &= ~(1 << 0);
if (uart_regs->ucon & (1 << 8)) {
s3c2410_intc_sub_deassert(x49gp, uart_regs->int_rxd);
}
break;
}
return *(reg->datap);
}
static void
s3c2410_uart_write(void *opaque, target_phys_addr_t offset, uint32_t data)
{
s3c2410_uart_reg_t *uart_regs = opaque;
x49gp_t *x49gp = uart_regs->x49gp;
s3c2410_offset_t *reg;
uint32_t base;
#ifdef DEBUG_S3C2410_UART
const char *module;
uint32_t mod_offset, ubrdivn, baud;
#endif
base = (offset & 0x0000c000) >> 14;
#ifdef DEBUG_S3C2410_UART
switch (base) {
case 0:
module = "s3c2410-uart0";
mod_offset = S3C2410_UART0_BASE;
break;
case 1:
module = "s3c2410-uart1";
mod_offset = S3C2410_UART1_BASE;
break;
case 2:
module = "s3c2410-uart2";
mod_offset = S3C2410_UART2_BASE;
break;
default:
return;
}
#endif
offset &= ~(0xffffc000);
if (! S3C2410_OFFSET_OK(uart_regs, offset)) {
return;
}
reg = S3C2410_OFFSET_ENTRY(uart_regs, offset);
#ifdef DEBUG_S3C2410_UART
printf("write %s [%08x] %s [%08lx] data %08x\n",
module, mod_offset, reg->name, (unsigned long) offset, data);
#endif
*(reg->datap) = data;
switch (offset) {
case S3C2410_UART0_UCON:
if (*(reg->datap) & (1 << 9))
s3c2410_intc_sub_assert(x49gp, uart_regs->int_txd, 1);
if (*(reg->datap) & (1 << 8))
s3c2410_intc_sub_deassert(x49gp, uart_regs->int_rxd);
break;
case S3C2410_UART0_UBRDIV:
#ifdef DEBUG_S3C2410_UART
ubrdivn = (data >> 0) & 0xffff;
if (uart_regs->ucon & (1 << 10)) {
baud = x49gp->UCLK / 16 / (ubrdivn + 1);
printf("%s: UEXTCLK %u, ubrdivn %u, baud %u\n",
module, x49gp->UCLK, ubrdivn, baud);
} else {
baud = x49gp->PCLK / 16 / (ubrdivn + 1);
printf("%s: PCLK %u, ubrdivn %u, baud %u\n",
module, x49gp->PCLK, ubrdivn, baud);
}
#endif
break;
case S3C2410_UART0_UTXH:
if (uart_regs->ucon & (1 << 9))
s3c2410_intc_sub_deassert(x49gp, uart_regs->int_txd);
uart_regs->utrstat |= (1 << 2) | (1 << 1);
if (uart_regs->ucon & (1 << 9))
s3c2410_intc_sub_assert(x49gp, uart_regs->int_txd, 1);
else
s3c2410_intc_sub_assert(x49gp, uart_regs->int_txd, 0);
if (uart_regs->ucon & (1 << 5)) {
uart_regs->urxh = data;
uart_regs->utrstat |= (1 << 0);
if (uart_regs->ucon & (1 << 8))
s3c2410_intc_sub_assert(x49gp, uart_regs->int_rxd, 1);
else
s3c2410_intc_sub_assert(x49gp, uart_regs->int_rxd, 0);
} else if (base == 2) {
uart_regs->urxh = data;
uart_regs->utrstat |= (1 << 0);
if (uart_regs->ucon & (1 << 8))
s3c2410_intc_sub_assert(x49gp, uart_regs->int_rxd, 1);
else
s3c2410_intc_sub_assert(x49gp, uart_regs->int_rxd, 0);
}
break;
}
}
static int
s3c2410_uart_load(x49gp_module_t *module, GKeyFile *key)
{
s3c2410_uart_reg_t *uart_regs = module->user_data;
s3c2410_offset_t *reg;
int error = 0;
int i;
#ifdef DEBUG_X49GP_MODULES
printf("%s: %s:%u\n", module->name, __FUNCTION__, __LINE__);
#endif
for (i = 0; i < uart_regs->nr_regs; i++) {
reg = &uart_regs->regs[i];
if (NULL == reg->name)
continue;
if (x49gp_module_get_u32(module, key, reg->name,
reg->reset, reg->datap))
error = -EAGAIN;
}
return error;
}
static int
s3c2410_uart_save(x49gp_module_t *module, GKeyFile *key)
{
s3c2410_uart_reg_t *uart_regs = module->user_data;
s3c2410_offset_t *reg;
int i;
#ifdef DEBUG_X49GP_MODULES
printf("%s: %s:%u\n", module->name, __FUNCTION__, __LINE__);
#endif
for (i = 0; i < uart_regs->nr_regs; i++) {
reg = &uart_regs->regs[i];
if (NULL == reg->name)
continue;
x49gp_module_set_u32(module, key, reg->name, *(reg->datap));
}
return 0;
}
static int
s3c2410_uart_reset(x49gp_module_t *module, x49gp_reset_t reset)
{
s3c2410_uart_reg_t *uart_regs = module->user_data;
s3c2410_offset_t *reg;
int i;
#ifdef DEBUG_X49GP_MODULES
printf("%s: %s:%u\n", module->name, __FUNCTION__, __LINE__);
#endif
for (i = 0; i < uart_regs->nr_regs; i++) {
reg = &uart_regs->regs[i];
if (NULL == reg->name)
continue;
*(reg->datap) = reg->reset;
}
return 0;
}
static CPUReadMemoryFunc *s3c2410_uart_readfn[] =
{
s3c2410_uart_read,
s3c2410_uart_read,
s3c2410_uart_read
};
static CPUWriteMemoryFunc *s3c2410_uart_writefn[] =
{
s3c2410_uart_write,
s3c2410_uart_write,
s3c2410_uart_write
};
static int
s3c2410_uart_init(x49gp_module_t *module)
{
s3c2410_uart_reg_t *uart_regs = module->user_data;
int iotype;
#ifdef DEBUG_X49GP_MODULES
printf("%s: %s:%u\n", module->name, __FUNCTION__, __LINE__);
#endif
#ifdef QEMU_OLD
iotype = cpu_register_io_memory(0, s3c2410_uart_readfn,
s3c2410_uart_writefn, uart_regs);
#else
iotype = cpu_register_io_memory(s3c2410_uart_readfn,
s3c2410_uart_writefn, uart_regs);
#endif
printf("%s: iotype %08x\n", __FUNCTION__, iotype);
cpu_register_physical_memory(S3C2410_UART0_BASE, S3C2410_MAP_SIZE, iotype);
return 0;
}
static int
s3c2410_uart_exit(x49gp_module_t *module)
{
s3c2410_uart_reg_t *uart_regs;
#ifdef DEBUG_X49GP_MODULES
printf("%s: %s:%u\n", module->name, __FUNCTION__, __LINE__);
#endif
if (module->user_data) {
uart_regs = module->user_data;
if (uart_regs->regs)
free(uart_regs->regs);
}
x49gp_module_unregister(module);
free(module);
return 0;
}
int
x49gp_s3c2410_uart_init(x49gp_t *x49gp)
{
s3c2410_uart_t *uart;
x49gp_module_t *module;
uart = malloc(sizeof(s3c2410_uart_t));
if (NULL == uart) {
fprintf(stderr, "%s:%u: Out of memory\n",
__FUNCTION__, __LINE__);
return -ENOMEM;
}
memset(uart, 0, sizeof(s3c2410_uart_t));
if (s3c2410_uart_data_init(uart)) {
free(uart);
return -ENOMEM;
}
uart->uart[0].x49gp = x49gp;
uart->uart[1].x49gp = x49gp;
uart->uart[2].x49gp = x49gp;
if (x49gp_module_init(x49gp, "s3c2410-uart0",
s3c2410_uart_init,
s3c2410_uart_exit,
s3c2410_uart_reset,
s3c2410_uart_load,
s3c2410_uart_save,
&uart->uart[0], &module)) {
return -1;
}
if (x49gp_module_register(module)) {
return -1;
}
if (x49gp_module_init(x49gp, "s3c2410-uart1",
s3c2410_uart_init,
s3c2410_uart_exit,
s3c2410_uart_reset,
s3c2410_uart_load,
s3c2410_uart_save,
&uart->uart[1], &module)) {
return -1;
}
if (x49gp_module_register(module)) {
return -1;
}
if (x49gp_module_init(x49gp, "s3c2410-uart2",
s3c2410_uart_init,
s3c2410_uart_exit,
s3c2410_uart_reset,
s3c2410_uart_load,
s3c2410_uart_save,
&uart->uart[2], &module)) {
return -1;
}
if (x49gp_module_register(module)) {
return -1;
}
return 0;
}