x49gp/s3c2410_io_port.c

727 lines
15 KiB
C

/* $Id: s3c2410_io_port.c,v 1.14 2008/12/11 12:18:17 ecd Exp $
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <x49gp.h>
#include <s3c2410.h>
#include <s3c2410_intc.h>
#include <byteorder.h>
typedef struct {
uint32_t gpacon;
uint32_t gpadat;
uint32_t gpbcon;
uint32_t gpbdat;
uint32_t gpbup;
uint32_t gpccon;
uint32_t gpcdat;
uint32_t gpcup;
uint32_t gpdcon;
uint32_t gpddat;
uint32_t gpdup;
uint32_t gpecon;
uint32_t gpedat;
uint32_t gpeup;
uint32_t gpfcon;
uint32_t gpfdat;
uint32_t gpfup;
uint32_t gpgcon;
uint32_t gpgdat;
uint32_t gpgup;
uint32_t gphcon;
uint32_t gphdat;
uint32_t gphup;
uint32_t misccr;
uint32_t dclkcon;
uint32_t extint0;
uint32_t extint1;
uint32_t extint2;
uint32_t eintflt0;
uint32_t eintflt1;
uint32_t eintflt2;
uint32_t eintflt3;
uint32_t eintmask;
uint32_t eintpend;
uint32_t gstatus0;
uint32_t gstatus1;
uint32_t gstatus2;
uint32_t gstatus3;
uint32_t gstatus4;
unsigned int nr_regs;
s3c2410_offset_t *regs;
x49gp_t *x49gp;
} s3c2410_io_port_t;
static int
s3c2410_io_port_data_init(s3c2410_io_port_t *io)
{
s3c2410_offset_t regs[] = {
S3C2410_OFFSET(IO_PORT, GPACON, 0x007fffff, io->gpacon),
S3C2410_OFFSET(IO_PORT, GPADAT, 0x00000000, io->gpadat),
S3C2410_OFFSET(IO_PORT, GPBCON, 0x00000000, io->gpbcon),
S3C2410_OFFSET(IO_PORT, GPBDAT, 0x00000000, io->gpbdat),
S3C2410_OFFSET(IO_PORT, GPBUP, 0x00000000, io->gpbup),
S3C2410_OFFSET(IO_PORT, GPCCON, 0x00000000, io->gpccon),
S3C2410_OFFSET(IO_PORT, GPCDAT, 0x00000000, io->gpcdat),
S3C2410_OFFSET(IO_PORT, GPCUP, 0x00000000, io->gpcup),
S3C2410_OFFSET(IO_PORT, GPDCON, 0x00000000, io->gpdcon),
S3C2410_OFFSET(IO_PORT, GPDDAT, 0x0000038c, io->gpddat),
S3C2410_OFFSET(IO_PORT, GPDUP, 0x0000f000, io->gpdup),
S3C2410_OFFSET(IO_PORT, GPECON, 0x00000000, io->gpecon),
S3C2410_OFFSET(IO_PORT, GPEDAT, 0x0000c7c0, io->gpedat),
S3C2410_OFFSET(IO_PORT, GPEUP, 0x00000000, io->gpeup),
S3C2410_OFFSET(IO_PORT, GPFCON, 0x00000000, io->gpfcon),
S3C2410_OFFSET(IO_PORT, GPFDAT, 0x00000008, io->gpfdat),
S3C2410_OFFSET(IO_PORT, GPFUP, 0x00000000, io->gpfup),
S3C2410_OFFSET(IO_PORT, GPGCON, 0x00000000, io->gpgcon),
S3C2410_OFFSET(IO_PORT, GPGDAT, 0x0000fffe, io->gpgdat),
S3C2410_OFFSET(IO_PORT, GPGUP, 0x0000f800, io->gpgup),
S3C2410_OFFSET(IO_PORT, GPHCON, 0x00000000, io->gphcon),
S3C2410_OFFSET(IO_PORT, GPHDAT, 0x00000000, io->gphdat),
S3C2410_OFFSET(IO_PORT, GPHUP, 0x00000000, io->gphup),
S3C2410_OFFSET(IO_PORT, MISCCR, 0x00010330, io->misccr),
S3C2410_OFFSET(IO_PORT, DCLKCON, 0x00000000, io->dclkcon),
S3C2410_OFFSET(IO_PORT, EXTINT0, 0x00000000, io->extint0),
S3C2410_OFFSET(IO_PORT, EXTINT1, 0x00000000, io->extint1),
S3C2410_OFFSET(IO_PORT, EXTINT2, 0x00000000, io->extint2),
S3C2410_OFFSET(IO_PORT, EINTFLT0, 0x00000000, io->eintflt0),
S3C2410_OFFSET(IO_PORT, EINTFLT1, 0x00000000, io->eintflt1),
S3C2410_OFFSET(IO_PORT, EINTFLT2, 0x00000000, io->eintflt2),
S3C2410_OFFSET(IO_PORT, EINTFLT3, 0x00000000, io->eintflt3),
S3C2410_OFFSET(IO_PORT, EINTMASK, 0x00fffff0, io->eintmask),
S3C2410_OFFSET(IO_PORT, EINTPEND, 0x00000000, io->eintpend),
S3C2410_OFFSET(IO_PORT, GSTATUS0, 0x00000001, io->gstatus0),
S3C2410_OFFSET(IO_PORT, GSTATUS1, 0x32410002, io->gstatus1),
S3C2410_OFFSET(IO_PORT, GSTATUS2, 0x00000001, io->gstatus2),
S3C2410_OFFSET(IO_PORT, GSTATUS3, 0x00000000, io->gstatus3),
S3C2410_OFFSET(IO_PORT, GSTATUS4, 0x00000000, io->gstatus4)
};
memset(io, 0, sizeof(s3c2410_io_port_t));
io->regs = malloc(sizeof(regs));
if (NULL == io->regs) {
fprintf(stderr, "%s:%u: Out of memory\n",
__FUNCTION__, __LINE__);
return -ENOMEM;
}
memcpy(io->regs, regs, sizeof(regs));
io->nr_regs = sizeof(regs) / sizeof(regs[0]);
return 0;
}
static uint32_t
s3c2410_scan_keys(x49gp_t *x49gp, uint32_t gpgcon, uint32_t gpgdat)
{
uint32_t result;
int col, row;
result = 0xfffe | (gpgdat & 1);
for (col = 0; col < 8; col++) {
switch ((gpgcon >> (2 * (col + 8))) & 3) {
case 0: /* Input */
case 2: /* Interrupt */
case 3: /* Reserved */
break;
case 1: /* Output */
result &= ~(1 << (col + 8));
result |= gpgdat & (1 << (col + 8));
if (0 == (gpgdat & (1 << (col + 8)))) {
result &= ~(x49gp->keybycol[col]);
}
break;
}
}
for (row = 1; row < 8; row++) {
switch ((gpgcon >> (2 * row)) & 3) {
case 0: /* Input */
case 2: /* Interrupt */
case 3: /* Reserved */
break;
case 1: /* Output */
result &= ~(1 << row);
result |= gpgdat & (1 << row);
if (0 == (gpgdat & (1 << row))) {
result &= ~(x49gp->keybyrow[row] << 8);
}
break;
}
}
return result;
}
static uint32_t
s3c2410_io_port_read(void *opaque, target_phys_addr_t offset)
{
s3c2410_io_port_t *io = opaque;
s3c2410_offset_t *reg;
#ifdef QEMU_OLD
offset -= S3C2410_IO_PORT_BASE;
#endif
if (! S3C2410_OFFSET_OK(io, offset)) {
fprintf(stderr, "%s:%u: offset %08x not OK\n", __FUNCTION__, __LINE__, offset);
abort();
return ~(0);
}
reg = S3C2410_OFFSET_ENTRY(io, offset);
switch (offset) {
case S3C2410_IO_PORT_MISCCR:
// if (io->x49gp->arm->NresetSig != LOW) {
*(reg->datap) |= 0x00010000;
// }
break;
case S3C2410_IO_PORT_GPCDAT:
if (0 == ((io->gpccon >> 30) & 3)) {
*(reg->datap) |= 0x8000;
}
if (0 == ((io->gpccon >> 28) & 3)) {
*(reg->datap) |= 0x4000;
}
if (0 == ((io->gpccon >> 26) & 3)) {
*(reg->datap) |= 0x2000;
}
if (0 == ((io->gpccon >> 24) & 3)) {
*(reg->datap) |= 0x1000;
}
break;
case S3C2410_IO_PORT_GPDDAT:
if (0 == ((io->gpdcon >> 6) & 3)) {
*(reg->datap) |= 0x0008;
}
break;
case S3C2410_IO_PORT_GPEDAT:
if (0 == ((io->gpecon >> 30) & 3)) {
*(reg->datap) |= 0x8000;
}
if (0 == ((io->gpecon >> 28) & 3)) {
*(reg->datap) |= 0x4000;
}
break;
case S3C2410_IO_PORT_GPFDAT:
#if 1
if (1 != ((io->gpfcon >> 6) & 3)) {
*(reg->datap) |= 0x0008;
}
#endif
break;
case S3C2410_IO_PORT_GPGDAT:
*(reg->datap) = s3c2410_scan_keys(io->x49gp, io->gpgcon, *(reg->datap));
break;
case S3C2410_IO_PORT_GPHDAT:
if (0 == ((io->gphcon >> 14) & 3)) {
*(reg->datap) |= 0x80;
}
if (0 == ((io->gphcon >> 12) & 3)) {
*(reg->datap) &= ~(0x40);
}
break;
}
#ifdef DEBUG_S3C2410_IO_PORT
printf("read %s [%08x] %s [%08x] data %08x\n",
"s3c2410-io-port", S3C2410_IO_PORT_BASE,
reg->name, offset, *(reg->datap));
#endif
return *(reg->datap);
}
static void
s3c2410_io_port_write(void *opaque, target_phys_addr_t offset, uint32_t data)
{
s3c2410_io_port_t *io = opaque;
s3c2410_offset_t *reg;
uint32_t change;
static uint32_t lcd_data = 0;
#ifdef QEMU_OLD
offset -= S3C2410_IO_PORT_BASE;
#endif
if (! S3C2410_OFFSET_OK(io, offset)) {
return;
}
reg = S3C2410_OFFSET_ENTRY(io, offset);
#ifdef DEBUG_S3C2410_IO_PORT
printf("write %s [%08x] %s [%08x] data %08x\n",
"s3c2410-io-port", S3C2410_IO_PORT_BASE,
reg->name, offset, data);
#endif
switch (offset) {
case S3C2410_IO_PORT_GPDDAT:
change = *(reg->datap) ^ data;
*(reg->datap) = data;
if (!(data & 0x200) && (change & 0x200)) {
lcd_data = 0;
}
if (!(data & 0x200) && (data & 0x2000) && (change & 0x2000)) {
#ifdef DEBUG_S3C2410_IO_PORT
printf("IO_PORT GPDDAT: clk0 rise: data %u\n",
(data >> 12) & 1);
#endif
lcd_data <<= 1;
lcd_data |= (data >> 12) & 1;
}
if ((data & 0x200) && (change & 0x200)) {
#ifdef DEBUG_S3C2410_IO_PORT
printf("IO_PORT GPDDAT: cs0 rise: data %04x\n",
lcd_data);
#endif
}
break;
case S3C2410_IO_PORT_MISCCR:
*(reg->datap) = data;
if (!(*(reg->datap) & 0x00010000)) {
*(reg->datap) = 0x10330;
// if (io->x49gp->arm->NresetSig != LOW) {
// io->x49gp->arm->NresetSig = LOW;
// io->x49gp->arm->Exception++;
// }
}
break;
case S3C2410_IO_PORT_GSTATUS0:
case S3C2410_IO_PORT_GSTATUS1:
/* read only */
break;
case S3C2410_IO_PORT_GSTATUS2:
*(reg->datap) &= ~(data & 7);
break;
case S3C2410_IO_PORT_EINTPEND:
*(reg->datap) &= ~(data);
if (0 == (*(reg->datap) & 0x000000f0))
s3c2410_intc_deassert(io->x49gp, EINT4_7);
if (0 == (*(reg->datap) & 0x00ffff00))
s3c2410_intc_deassert(io->x49gp, EINT8_23);
break;
default:
*(reg->datap) = data;
break;
}
}
void
s3c2410_io_port_g_set_bit(x49gp_t *x49gp, int n, uint32_t set)
{
s3c2410_io_port_t *io = x49gp->s3c2410_io_port;
uint32_t value, change;
int pending, level;
if (n > 7)
return;
// g_mutex_lock(x49gp->memlock);
io->gpgdat = s3c2410_scan_keys(x49gp, io->gpgcon, io->gpgdat);
#ifdef DEBUG_S3C2410_IO_PORT
printf("IO_PORT: %s GPG bit %u\n", set ? "assert" : "deassert", n);
printf("IO_PORT: GPGCON %08x, GPGDAT %08x\n", io->gpgcon, io->gpgdat);
#endif
if (0 == set) {
value = 1;
} else {
if ((((io->gpgcon >> (2 * (n + 8))) & 3) == 1) &&
(((io->gpgdat >> (n + 8)) & 1) == 0)) {
value = 0;
} else {
value = 1;
}
}
#ifdef DEBUG_S3C2410_IO_PORT
printf("IO_PORT: GPG bit %u value: %u\n", n, value);
#endif
change = 0;
switch ((io->gpgcon >> (2 * n)) & 3) {
case 0: /* Input */
io->gpgdat &= ~(1 << n);
io->gpgdat |= (value << n);
goto out;
case 2: /* Interrupt */
change = io->gpgdat ^ (value << n);
io->gpgdat &= ~(1 << n);
io->gpgdat |= (value << n);
break;
case 1: /* Output */
case 3: /* Reserved */
goto out;
}
#ifdef DEBUG_S3C2410_IO_PORT
printf("IO_PORT: GPGDAT %08x, change %08x\n", io->gpgdat, change);
#endif
pending = -1;
level = 0;
switch ((io->extint1 >> (4 * n)) & 7) {
case 0: /* Low Level */
if (!(io->gpgdat & (1 << n)))
pending = n;
level = 1;
break;
case 1: /* High Level */
if (io->gpgdat & (1 << n))
pending = n;
level = 1;
break;
case 2: /* Falling Edge */
case 3:
if ((change & (1 << n)) && !(io->gpgdat & (1 << n)))
pending = n;
break;
case 4: /* Rising Edge */
case 5:
if ((change & (1 << n)) && (io->gpgdat & (1 << n)))
pending = n;
break;
case 6: /* Any Edge */
case 7:
if (change & (1 << n))
pending = n;
break;
}
#ifdef DEBUG_S3C2410_IO_PORT
printf("IO_PORT: IRQ: %d, (Level %u)\n", pending, level);
#endif
if (-1 == pending)
goto out;
io->eintpend |= 1 << (n + 8);
if (io->eintpend & ~(io->eintmask))
s3c2410_intc_assert(x49gp, EINT8_23, 1);
out:
// g_mutex_unlock(x49gp->memlock);
return;
}
void
s3c2410_io_port_f_set_bit(x49gp_t *x49gp, int n, uint32_t value)
{
s3c2410_io_port_t *io = x49gp->s3c2410_io_port;
uint32_t change;
int pending, level;
if (n > 7)
return;
// g_mutex_lock(x49gp->memlock);
change = 0;
switch ((io->gpfcon >> (2 * n)) & 3) {
case 0: /* Input */
io->gpfdat &= ~(1 << n);
io->gpfdat |= (value << n);
goto out;
case 2: /* Interrupt */
change = io->gpfdat ^ (value << n);
io->gpfdat &= ~(1 << n);
io->gpfdat |= (value << n);
break;
case 1: /* Output */
case 3: /* Reserved */
goto out;
}
pending = -1;
level = 0;
switch ((io->extint0 >> (4 * n)) & 7) {
case 0: /* Low Level */
if (!(io->gpfdat & (1 << n)))
pending = n;
level = 1;
break;
case 1: /* High Level */
if (io->gpfdat & (1 << n))
pending = n;
level = 1;
break;
case 2: /* Falling Edge */
case 3:
if ((change & (1 << n)) && !(io->gpfdat & (1 << n)))
pending = n;
break;
case 4: /* Rising Edge */
case 5:
if ((change & (1 << n)) && (io->gpfdat & (1 << n)))
pending = n;
break;
case 6: /* Any Edge */
case 7:
if (change & (1 << n))
pending = n;
break;
}
if (-1 == pending)
goto out;
switch (n) {
case 0:
s3c2410_intc_assert(x49gp, EINT0, level);
break;
case 1:
s3c2410_intc_assert(x49gp, EINT1, level);
break;
case 2:
s3c2410_intc_assert(x49gp, EINT2, level);
break;
case 3:
s3c2410_intc_assert(x49gp, EINT3, level);
break;
case 4:
case 5:
case 6:
case 7:
io->eintpend |= (1 << n);
if (io->eintpend & ~(io->eintmask))
s3c2410_intc_assert(x49gp, EINT4_7, 1);
break;
}
out:
// g_mutex_unlock(x49gp->memlock);
return;
}
static int
s3c2410_io_port_load(x49gp_module_t *module, GKeyFile *key)
{
s3c2410_io_port_t *io = 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 < io->nr_regs; i++) {
reg = &io->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_io_port_save(x49gp_module_t *module, GKeyFile *key)
{
s3c2410_io_port_t *io = 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 < io->nr_regs; i++) {
reg = &io->regs[i];
if (NULL == reg->name)
continue;
x49gp_module_set_u32(module, key, reg->name, *(reg->datap));
}
return 0;
}
static int
s3c2410_io_port_reset(x49gp_module_t *module, x49gp_reset_t reset)
{
s3c2410_io_port_t *io = module->user_data;
s3c2410_offset_t *reg;
int i;
#ifdef DEBUG_X49GP_MODULES
printf("%s: %s:%u\n", module->name, __FUNCTION__, __LINE__);
#endif
if (reset == X49GP_RESET_POWER_OFF) {
io->gstatus2 = 2;
return 0;
}
for (i = 0; i < io->nr_regs; i++) {
reg = &io->regs[i];
if (NULL == reg->name)
continue;
*(reg->datap) = reg->reset;
}
if (reset == X49GP_RESET_WATCHDOG) {
io->gstatus2 = 4;
}
return 0;
}
static CPUReadMemoryFunc *s3c2410_io_port_readfn[] =
{
s3c2410_io_port_read,
s3c2410_io_port_read,
s3c2410_io_port_read
};
static CPUWriteMemoryFunc *s3c2410_io_port_writefn[] =
{
s3c2410_io_port_write,
s3c2410_io_port_write,
s3c2410_io_port_write
};
static int
s3c2410_io_port_init(x49gp_module_t *module)
{
s3c2410_io_port_t *io;
int iotype;
#ifdef DEBUG_X49GP_MODULES
printf("%s: %s:%u\n", module->name, __FUNCTION__, __LINE__);
#endif
io = malloc(sizeof(s3c2410_io_port_t));
if (NULL == io) {
fprintf(stderr, "%s:%u: Out of memory\n",
__FUNCTION__, __LINE__);
return -ENOMEM;
}
if (s3c2410_io_port_data_init(io)) {
free(io);
return -ENOMEM;
}
module->user_data = io;
module->x49gp->s3c2410_io_port = io;
io->x49gp = module->x49gp;
#ifdef QEMU_OLD
iotype = cpu_register_io_memory(0, s3c2410_io_port_readfn,
s3c2410_io_port_writefn, io);
#else
iotype = cpu_register_io_memory(s3c2410_io_port_readfn,
s3c2410_io_port_writefn, io);
#endif
printf("%s: iotype %08x\n", __FUNCTION__, iotype);
cpu_register_physical_memory(S3C2410_IO_PORT_BASE, S3C2410_MAP_SIZE, iotype);
return 0;
}
static int
s3c2410_io_port_exit(x49gp_module_t *module)
{
s3c2410_io_port_t *io;
#ifdef DEBUG_X49GP_MODULES
printf("%s: %s:%u\n", module->name, __FUNCTION__, __LINE__);
#endif
if (module->user_data) {
io = module->user_data;
if (io->regs)
free(io->regs);
free(io);
}
x49gp_module_unregister(module);
free(module);
return 0;
}
int
x49gp_s3c2410_io_port_init(x49gp_t *x49gp)
{
x49gp_module_t *module;
if (x49gp_module_init(x49gp, "s3c2410-io-port",
s3c2410_io_port_init,
s3c2410_io_port_exit,
s3c2410_io_port_reset,
s3c2410_io_port_load,
s3c2410_io_port_save,
NULL, &module)) {
return -1;
}
return x49gp_module_register(module);
}