forked from Miroirs/x49gp
478 lines
11 KiB
C
478 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
|
|
|
|
iotype = cpu_register_io_memory(s3c2410_uart_readfn,
|
|
s3c2410_uart_writefn, uart_regs);
|
|
#ifdef DEBUG_S3C2410_UART
|
|
printf("%s: iotype %08x\n", __FUNCTION__, iotype);
|
|
#endif
|
|
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;
|
|
}
|