/* $Id: s3c2410_io_port.c,v 1.14 2008/12/11 12:18:17 ecd Exp $ */ #include #include #include #include #include #include #include #include #include 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 %08lx not OK\n", __FUNCTION__, __LINE__, (unsigned long) 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; #ifdef DEBUG_S3C2410_IO_PORT int level = 0; #endif 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; switch ((io->extint1 >> (4 * n)) & 7) { case 0: /* Low Level */ if (!(io->gpgdat & (1 << n))) pending = n; #ifdef DEBUG_S3C2410_IO_PORT level = 1; #endif break; case 1: /* High Level */ if (io->gpgdat & (1 << n)) pending = n; #ifdef DEBUG_S3C2410_IO_PORT level = 1; #endif 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); }