diff options
Diffstat (limited to 'hw/omap.c')
-rw-r--r-- | hw/omap.c | 177 |
1 files changed, 177 insertions, 0 deletions
@@ -3065,6 +3065,179 @@ void omap_mpuio_key(struct omap_mpuio_s *s, int row, int col, int down) omap_mpuio_kbd_update(s); } +/* General-Purpose I/O */ +struct omap_gpio_s { + target_phys_addr_t base; + qemu_irq irq; + qemu_irq *in; + qemu_irq handler[16]; + + uint16_t inputs; + uint16_t outputs; + uint16_t dir; + uint16_t edge; + uint16_t mask; + uint16_t ints; +}; + +static void omap_gpio_set(void *opaque, int line, int level) +{ + struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; + uint16_t prev = s->inputs; + + if (level) + s->inputs |= 1 << line; + else + s->inputs &= ~(1 << line); + + if (((s->edge & s->inputs & ~prev) | (~s->edge & ~s->inputs & prev)) & + (1 << line) & s->dir & ~s->mask) { + s->ints |= 1 << line; + qemu_irq_raise(s->irq); + } +} + +static uint32_t omap_gpio_read(void *opaque, target_phys_addr_t addr) +{ + struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; + int offset = addr - s->base; + uint16_t ret; + + switch (offset) { + case 0x00: /* DATA_INPUT */ + return s->inputs; + + case 0x04: /* DATA_OUTPUT */ + return s->outputs; + + case 0x08: /* DIRECTION_CONTROL */ + return s->dir; + + case 0x0c: /* INTERRUPT_CONTROL */ + return s->edge; + + case 0x10: /* INTERRUPT_MASK */ + return s->mask; + + case 0x14: /* INTERRUPT_STATUS */ + return s->ints; + } + + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_gpio_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; + int offset = addr - s->base; + uint16_t diff; + int ln; + + switch (offset) { + case 0x00: /* DATA_INPUT */ + OMAP_RO_REG(addr); + return; + + case 0x04: /* DATA_OUTPUT */ + diff = s->outputs ^ (value & ~s->dir); + s->outputs = value; + value &= ~s->dir; + while ((ln = ffs(diff))) { + ln --; + if (s->handler[ln]) + qemu_set_irq(s->handler[ln], (value >> ln) & 1); + diff &= ~(1 << ln); + } + break; + + case 0x08: /* DIRECTION_CONTROL */ + diff = s->outputs & (s->dir ^ value); + s->dir = value; + + value = s->outputs & ~s->dir; + while ((ln = ffs(diff))) { + ln --; + if (s->handler[ln]) + qemu_set_irq(s->handler[ln], (value >> ln) & 1); + diff &= ~(1 << ln); + } + break; + + case 0x0c: /* INTERRUPT_CONTROL */ + s->edge = value; + break; + + case 0x10: /* INTERRUPT_MASK */ + s->mask = value; + break; + + case 0x14: /* INTERRUPT_STATUS */ + s->ints &= ~value; + if (!s->ints) + qemu_irq_lower(s->irq); + break; + + default: + OMAP_BAD_REG(addr); + return; + } +} + +static CPUReadMemoryFunc *omap_gpio_readfn[] = { + omap_gpio_read, + omap_badwidth_read32, + omap_badwidth_read32, +}; + +static CPUWriteMemoryFunc *omap_gpio_writefn[] = { + omap_gpio_write, + omap_badwidth_write32, + omap_badwidth_write32, +}; + +void omap_gpio_reset(struct omap_gpio_s *s) +{ + s->inputs = 0; + s->outputs = ~0; + s->dir = ~0; + s->edge = ~0; + s->mask = ~0; + s->ints = 0; +} + +struct omap_gpio_s *omap_gpio_init(target_phys_addr_t base, + qemu_irq irq, omap_clk clk) +{ + int iomemtype; + struct omap_gpio_s *s = (struct omap_gpio_s *) + qemu_mallocz(sizeof(struct omap_gpio_s)); + + s->base = base; + s->irq = irq; + s->in = qemu_allocate_irqs(omap_gpio_set, s, 16); + omap_gpio_reset(s); + + iomemtype = cpu_register_io_memory(0, omap_gpio_readfn, + omap_gpio_writefn, s); + cpu_register_physical_memory(s->base, 0x1000, iomemtype); + + return s; +} + +qemu_irq *omap_gpio_in_get(struct omap_gpio_s *s) +{ + return s->in; +} + +void omap_gpio_out_set(struct omap_gpio_s *s, int line, qemu_irq handler) +{ + if (line >= 16 || line < 0) + cpu_abort(cpu_single_env, "%s: No GPIO line %i\n", __FUNCTION__, line); + s->handler[line] = handler; +} + /* General chip reset */ static void omap_mpu_reset(void *opaque) { @@ -3093,6 +3266,7 @@ static void omap_mpu_reset(void *opaque) omap_uart_reset(mpu->uart3); omap_mmc_reset(mpu->mmc); omap_mpuio_reset(mpu->mpuio); + omap_gpio_reset(mpu->gpio); cpu_reset(mpu->env); } @@ -3208,6 +3382,9 @@ struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size, s->irq[1][OMAP_INT_KEYBOARD], s->irq[1][OMAP_INT_MPUIO], s->wakeup, omap_findclk(s, "clk32-kHz")); + s->gpio = omap_gpio_init(0xfffcf000, s->irq[1][OMAP_INT_KEYBOARD], + omap_findclk(s, "mpuper_ck")); + qemu_register_reset(omap_mpu_reset, s); return s; |