diff -r ffa851df0825 -r 2fb8b9db1c86 symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/pl061.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/pl061.c Fri Jul 31 15:01:17 2009 +0100 @@ -0,0 +1,313 @@ +/* + * Arm PrimeCell PL061 General Purpose IO with additional + * Luminary Micro Stellaris bits. + * + * Copyright (c) 2007 CodeSourcery. + * Written by Paul Brook + * + * This code is licenced under the GPL. + */ + +#include "hw.h" +#include "primecell.h" + +//#define DEBUG_PL061 1 + +#ifdef DEBUG_PL061 +#define DPRINTF(fmt, args...) \ +do { printf("pl061: " fmt , ##args); } while (0) +#define BADF(fmt, args...) \ +do { fprintf(stderr, "pl061: error: " fmt , ##args); exit(1);} while (0) +#else +#define DPRINTF(fmt, args...) do {} while(0) +#define BADF(fmt, args...) \ +do { fprintf(stderr, "pl061: error: " fmt , ##args);} while (0) +#endif + +static const uint8_t pl061_id[12] = + { 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 }; + +typedef struct { + int locked; + uint8_t data; + uint8_t old_data; + uint8_t dir; + uint8_t isense; + uint8_t ibe; + uint8_t iev; + uint8_t im; + uint8_t istate; + uint8_t afsel; + uint8_t dr2r; + uint8_t dr4r; + uint8_t dr8r; + uint8_t odr; + uint8_t pur; + uint8_t pdr; + uint8_t slr; + uint8_t den; + uint8_t cr; + uint8_t float_high; + qemu_irq irq; + qemu_irq out[8]; +} pl061_state; + +static void pl061_update(pl061_state *s) +{ + uint8_t changed; + uint8_t mask; + uint8_t out; + int i; + + /* Outputs float high. */ + /* FIXME: This is board dependent. */ + out = (s->data & s->dir) | ~s->dir; + changed = s->old_data ^ out; + if (!changed) + return; + + s->old_data = out; + for (i = 0; i < 8; i++) { + mask = 1 << i; + if ((changed & mask) && s->out) { + DPRINTF("Set output %d = %d\n", i, (out & mask) != 0); + qemu_set_irq(s->out[i], (out & mask) != 0); + } + } + + /* FIXME: Implement input interrupts. */ +} + +static uint32_t pl061_read(void *opaque, target_phys_addr_t offset) +{ + pl061_state *s = (pl061_state *)opaque; + + if (offset >= 0xfd0 && offset < 0x1000) { + return pl061_id[(offset - 0xfd0) >> 2]; + } + if (offset < 0x400) { + return s->data & (offset >> 2); + } + switch (offset) { + case 0x400: /* Direction */ + return s->dir; + case 0x404: /* Interrupt sense */ + return s->isense; + case 0x408: /* Interrupt both edges */ + return s->ibe; + case 0x40c: /* Interupt event */ + return s->iev; + case 0x410: /* Interrupt mask */ + return s->im; + case 0x414: /* Raw interrupt status */ + return s->istate; + case 0x418: /* Masked interrupt status */ + return s->istate | s->im; + case 0x420: /* Alternate function select */ + return s->afsel; + case 0x500: /* 2mA drive */ + return s->dr2r; + case 0x504: /* 4mA drive */ + return s->dr4r; + case 0x508: /* 8mA drive */ + return s->dr8r; + case 0x50c: /* Open drain */ + return s->odr; + case 0x510: /* Pull-up */ + return s->pur; + case 0x514: /* Pull-down */ + return s->pdr; + case 0x518: /* Slew rate control */ + return s->slr; + case 0x51c: /* Digital enable */ + return s->den; + case 0x520: /* Lock */ + return s->locked; + case 0x524: /* Commit */ + return s->cr; + default: + cpu_abort (cpu_single_env, "pl061_read: Bad offset %x\n", + (int)offset); + return 0; + } +} + +static void pl061_write(void *opaque, target_phys_addr_t offset, + uint32_t value) +{ + pl061_state *s = (pl061_state *)opaque; + uint8_t mask; + + if (offset < 0x400) { + mask = (offset >> 2) & s->dir; + s->data = (s->data & ~mask) | (value & mask); + pl061_update(s); + return; + } + switch (offset) { + case 0x400: /* Direction */ + s->dir = value; + break; + case 0x404: /* Interrupt sense */ + s->isense = value; + break; + case 0x408: /* Interrupt both edges */ + s->ibe = value; + break; + case 0x40c: /* Interupt event */ + s->iev = value; + break; + case 0x410: /* Interrupt mask */ + s->im = value; + break; + case 0x41c: /* Interrupt clear */ + s->istate &= ~value; + break; + case 0x420: /* Alternate function select */ + mask = s->cr; + s->afsel = (s->afsel & ~mask) | (value & mask); + break; + case 0x500: /* 2mA drive */ + s->dr2r = value; + break; + case 0x504: /* 4mA drive */ + s->dr4r = value; + break; + case 0x508: /* 8mA drive */ + s->dr8r = value; + break; + case 0x50c: /* Open drain */ + s->odr = value; + break; + case 0x510: /* Pull-up */ + s->pur = value; + break; + case 0x514: /* Pull-down */ + s->pdr = value; + break; + case 0x518: /* Slew rate control */ + s->slr = value; + break; + case 0x51c: /* Digital enable */ + s->den = value; + break; + case 0x520: /* Lock */ + s->locked = (value != 0xacce551); + break; + case 0x524: /* Commit */ + if (!s->locked) + s->cr = value; + break; + default: + cpu_abort (cpu_single_env, "pl061_write: Bad offset %x\n", + (int)offset); + } + pl061_update(s); +} + +static void pl061_reset(pl061_state *s) +{ + s->locked = 1; + s->cr = 0xff; +} + +static void pl061_set_irq(void * opaque, int irq, int level) +{ + pl061_state *s = (pl061_state *)opaque; + uint8_t mask; + + mask = 1 << irq; + if ((s->dir & mask) == 0) { + s->data &= ~mask; + if (level) + s->data |= mask; + pl061_update(s); + } +} + +static CPUReadMemoryFunc *pl061_readfn[] = { + pl061_read, + pl061_read, + pl061_read +}; + +static CPUWriteMemoryFunc *pl061_writefn[] = { + pl061_write, + pl061_write, + pl061_write +}; + +static void pl061_save(QEMUFile *f, void *opaque) +{ + pl061_state *s = (pl061_state *)opaque; + + qemu_put_be32(f, s->locked); + qemu_put_be32(f, s->data); + qemu_put_be32(f, s->old_data); + qemu_put_be32(f, s->dir); + qemu_put_be32(f, s->isense); + qemu_put_be32(f, s->ibe); + qemu_put_be32(f, s->iev); + qemu_put_be32(f, s->im); + qemu_put_be32(f, s->istate); + qemu_put_be32(f, s->afsel); + qemu_put_be32(f, s->dr2r); + qemu_put_be32(f, s->dr4r); + qemu_put_be32(f, s->dr8r); + qemu_put_be32(f, s->odr); + qemu_put_be32(f, s->pur); + qemu_put_be32(f, s->pdr); + qemu_put_be32(f, s->slr); + qemu_put_be32(f, s->den); + qemu_put_be32(f, s->cr); + qemu_put_be32(f, s->float_high); +} + +static int pl061_load(QEMUFile *f, void *opaque, int version_id) +{ + pl061_state *s = (pl061_state *)opaque; + if (version_id != 1) + return -EINVAL; + + s->locked = qemu_get_be32(f); + s->data = qemu_get_be32(f); + s->old_data = qemu_get_be32(f); + s->dir = qemu_get_be32(f); + s->isense = qemu_get_be32(f); + s->ibe = qemu_get_be32(f); + s->iev = qemu_get_be32(f); + s->im = qemu_get_be32(f); + s->istate = qemu_get_be32(f); + s->afsel = qemu_get_be32(f); + s->dr2r = qemu_get_be32(f); + s->dr4r = qemu_get_be32(f); + s->dr8r = qemu_get_be32(f); + s->odr = qemu_get_be32(f); + s->pur = qemu_get_be32(f); + s->pdr = qemu_get_be32(f); + s->slr = qemu_get_be32(f); + s->den = qemu_get_be32(f); + s->cr = qemu_get_be32(f); + s->float_high = qemu_get_be32(f); + + return 0; +} + +/* Returns an array of inputs. */ +qemu_irq *pl061_init(uint32_t base, qemu_irq irq, qemu_irq **out) +{ + int iomemtype; + pl061_state *s; + + s = (pl061_state *)qemu_mallocz(sizeof(pl061_state)); + iomemtype = cpu_register_io_memory(0, pl061_readfn, + pl061_writefn, s); + cpu_register_physical_memory(base, 0x00001000, iomemtype); + s->irq = irq; + pl061_reset(s); + if (out) + *out = s->out; + + register_savevm("pl061_gpio", -1, 1, pl061_save, pl061_load, s); + return qemu_allocate_irqs(pl061_set_irq, s, 8); +}